1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2004-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% 21-module(ftp_SUITE). 22 23-include_lib("kernel/include/file.hrl"). 24-include_lib("common_test/include/ct.hrl"). 25 26%% Note: This directive should only be used in test suites. 27-compile(export_all). 28 29-define(FTP_USER, "anonymous"). 30-define(FTP_PASS(Cmnt), (fun({ok,__H}) -> "ftp_SUITE_"++Cmnt++"@" ++ __H; 31 (_) -> "ftp_SUITE_"++Cmnt++"@localhost" 32 end)(inet:gethostname()) 33 ). 34 35-define(BAD_HOST, "badhostname"). 36-define(BAD_USER, "baduser"). 37-define(BAD_DIR, "baddirectory"). 38 39-record(progress, { 40 current = 0, 41 total 42 }). 43 44%%-------------------------------------------------------------------- 45%% Common Test interface functions ----------------------------------- 46%%-------------------------------------------------------------------- 47suite() -> 48 [{timetrap,{seconds,20}}]. 49 50all() -> 51 [ 52 {group, ftp_passive}, 53 {group, ftp_active}, 54 {group, ftps_passive}, 55 {group, ftps_active}, 56 {group, ftp_sup}, 57 app, 58 appup, 59 error_ehost, 60 clean_shutdown 61 ]. 62 63groups() -> 64 [ 65 {ftp_passive, [], ftp_tests()}, 66 {ftp_active, [], ftp_tests()}, 67 {ftps_passive, [], ftp_tests()}, 68 {ftps_active, [], ftp_tests()}, 69 {ftp_sup, [], ftp_sup_tests()} 70 ]. 71 72ftp_tests()-> 73 [ 74 user, 75 bad_user, 76 pwd, 77 cd, 78 lcd, 79 ls, 80 nlist, 81 rename, 82 delete, 83 mkdir, 84 rmdir, 85 send, 86 send_3, 87 send_bin, 88 send_chunk, 89 append, 90 append_bin, 91 append_chunk, 92 recv, 93 recv_3, 94 recv_bin, 95 recv_bin_twice, 96 recv_chunk, 97 recv_chunk_twice, 98 recv_chunk_three_times, 99 recv_chunk_delay, 100 type, 101 quote, 102 error_elogin, 103 progress_report_send, 104 progress_report_recv, 105 not_owner, 106 unexpected_call, 107 unexpected_cast, 108 unexpected_bang 109 ]. 110 111ftp_sup_tests() -> 112 [ 113 start_ftp, 114 ftp_worker 115 ]. 116 117%%-------------------------------------------------------------------- 118 119%%% Config 120%%% key meaning 121%%% ................................................................ 122%%% ftpservers list of servers to check if they are available 123%%% The element is: 124%%% {Name, % string(). The os command name 125%%% Path, % string(). The os PATH syntax, e.g "/bin:/usr/bin" 126%%% StartCommand, % fun()->{ok,start_result()} | {error,string()}. 127%%% % The command to start the daemon with. 128%%% ChkUp, % fun(start_result()) -> string(). Os command to check 129%%% % if the server is running. [] if not running. 130%%% % The string in string() is suitable for logging. 131%%% StopCommand, % fun(start_result()) -> void(). The command to stop the daemon with. 132%%% AugmentFun, % fun(config()) -> config() Adds two funs for transforming names of files 133%%% % and directories to the form they are returned from this server 134%%% ServerHost, % string(). Mostly "localhost" 135%%% ServerPort % pos_integer() 136%%% } 137%%% 138 139-define(default_ftp_servers, 140 [{"vsftpd", 141 "/sbin:/usr/sbin:/usr/local/sbin", 142 fun(__CONF__, AbsName) -> 143 DataDir = proplists:get_value(data_dir,__CONF__), 144 ConfFile = filename:join(DataDir, "vsftpd.conf"), 145 PrivDir = proplists:get_value(priv_dir,__CONF__), 146 AnonRoot = PrivDir, 147 Cmd = [AbsName ++" "++filename:join(DataDir,"vsftpd.conf"), 148 " -oftpd_banner=erlang_otp_testing", 149 " -oanon_root=\"",AnonRoot,"\"", 150 " -orsa_cert_file=\"",filename:join(DataDir,"server-cert.pem"),"\"", 151 " -orsa_private_key_file=\"",filename:join(DataDir,"server-key.pem"),"\"" 152 ], 153 Result = os:cmd(Cmd), 154 ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p", 155 [case file:read_file(ConfFile) of 156 {ok,X} -> X; 157 _ -> "" 158 end, 159 Cmd, Result 160 ]), 161 case Result of 162 [] -> {ok,'dont care'}; 163 [Msg] -> {error,Msg} 164 end 165 end, 166 fun(_StartResult) -> os:cmd("ps ax | grep erlang_otp_testing | grep -v grep") 167 end, 168 fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`") 169 end, 170 fun(__CONF__) -> 171 AnonRoot = proplists:get_value(priv_dir,__CONF__), 172 [{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end}, 173 {id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__] 174 end, 175 "localhost", 176 9999 177 } 178 ] 179 ). 180 181 182init_per_suite(Config) -> 183 case find_executable(Config) of 184 false -> 185 {skip, "No ftp server found"}; 186 {ok,Data} -> 187 TstDir = filename:join(proplists:get_value(priv_dir,Config), "test"), 188 file:make_dir(TstDir), 189 %% make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)), 190 ftp_test_lib:make_cert_files(proplists:get_value(data_dir,Config)), 191 start_ftpd([{test_dir,TstDir}, 192 {ftpd_data,Data} 193 | Config]) 194 end. 195 196end_per_suite(Config) -> 197 ps_ftpd(Config), 198 stop_ftpd(Config), 199 ps_ftpd(Config), 200 ok. 201 202%%-------------------------------------------------------------------- 203init_per_group(Group, Config) when Group == ftps_active, 204 Group == ftps_passive -> 205 catch crypto:stop(), 206 try crypto:start() of 207 ok -> 208 Config 209 catch 210 _:_ -> 211 {skip, "Crypto did not start"} 212 end; 213init_per_group(ftp_sup, Config) -> 214 try ftp:start() of 215 ok -> 216 Config 217 catch 218 _:_ -> 219 {skip, "Ftp did not start"} 220 end; 221init_per_group(_Group, Config) -> 222 Config. 223 224 225end_per_group(ftp_sup, Config) -> 226 ftp:stop(), 227 Config; 228end_per_group(_Group, Config) -> 229 Config. 230 231%%-------------------------------------------------------------------- 232init_per_testcase(T, Config0) when T =:= app; T =:= appup -> 233 Config0; 234init_per_testcase(Case, Config0) -> 235 Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config0)), 236 237 %% Workaround for interoperability issues with vsftpd =< 3.0.2: 238 %% 239 %% vsftpd =< 3.0.2 does not support ECDHE ciphers and the ssl application 240 %% removed ciphers with RSA key exchange from its default cipher list. 241 %% To allow interoperability with old versions of vsftpd, cipher suites 242 %% with RSA key exchange are appended to the default cipher list. 243 All = ssl:cipher_suites(all, 'tlsv1.2'), 244 Default = ssl:cipher_suites(default, 'tlsv1.2'), 245 RSASuites = 246 ssl:filter_cipher_suites(All, [{key_exchange, fun(rsa) -> true; 247 (_) -> false end}]), 248 Suites = ssl:append_cipher_suites(RSASuites, Default), 249 TLS = [{tls,[{reuse_sessions,true},{ciphers, Suites}]}], 250 ACTIVE = [{mode,active}], 251 PASSIVE = [{mode,passive}], 252 CaseOpts = case Case of 253 progress_report_send -> [{progress, {?MODULE,progress,#progress{}}}]; 254 progress_report_recv -> [{progress, {?MODULE,progress,#progress{}}}]; 255 _ -> [] 256 end, 257 ExtraOpts = [verbose | CaseOpts], 258 Config = 259 case Group of 260 ftp_active -> ftp__open(Config0, ACTIVE ++ ExtraOpts); 261 ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ ExtraOpts); 262 ftp_passive -> ftp__open(Config0, PASSIVE ++ ExtraOpts); 263 ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ ExtraOpts); 264 ftp_sup -> ftp_start_service(Config0, ACTIVE ++ ExtraOpts); 265 undefined -> Config0 266 end, 267 case Case of 268 user -> Config; 269 bad_user -> Config; 270 error_elogin -> Config; 271 error_ehost -> Config; 272 clean_shutdown -> Config; 273 _ -> 274 Pid = proplists:get_value(ftp,Config), 275 ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ), 276 ok = ftp:cd(Pid, proplists:get_value(priv_dir,Config)), 277 Config 278 end. 279 280end_per_testcase(T, _Config) when T =:= app; T =:= appup -> ok; 281end_per_testcase(user, _Config) -> ok; 282end_per_testcase(bad_user, _Config) -> ok; 283end_per_testcase(error_elogin, _Config) -> ok; 284end_per_testcase(error_ehost, _Config) -> ok; 285end_per_testcase(clean_shutdown, _Config) -> ok; 286end_per_testcase(_Case, Config) -> 287 case proplists:get_value(tc_status,Config) of 288 ok -> ok; 289 _ -> 290 try ftp:latest_ctrl_response(proplists:get_value(ftp,Config)) 291 of 292 {ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S]) 293 catch 294 _:_ -> ok 295 end 296 end, 297 Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config)), 298 case Group of 299 ftp_sup -> 300 ftp_stop_service(Config); 301 _Else -> 302 ftp__close(Config) 303 end. 304 305%%-------------------------------------------------------------------- 306%% Test Cases -------------------------------------------------------- 307%%-------------------------------------------------------------------- 308app() -> 309 [{doc, "Test that the ftp app file is ok"}]. 310app(Config) when is_list(Config) -> 311 ok = ?t:app_test(ftp). 312 313%%-------------------------------------------------------------------- 314appup() -> 315 [{doc, "Test that the ftp appup file is ok"}]. 316appup(Config) when is_list(Config) -> 317 ok = ?t:appup_test(ftp). 318 319%%-------------------------------------------------------------------- 320 321user() -> [ 322 {doc, "Open an ftp connection to a host, and logon as anonymous ftp," 323 " then logoff"}]. 324user(Config) -> 325 Pid = proplists:get_value(ftp, Config), 326 ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon 327 ok = ftp:close(Pid), % logoff 328 {error,eclosed} = ftp:pwd(Pid), % check logoff result 329 ok. 330 331%%------------------------------------------------------------------------- 332bad_user() -> 333 [{doc, "Open an ftp connection to a host, and logon with bad user."}]. 334bad_user(Config) -> 335 Pid = proplists:get_value(ftp, Config), 336 {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")), 337 ok. 338 339%%------------------------------------------------------------------------- 340pwd() -> 341 [{doc, "Test ftp:pwd/1 & ftp:lpwd/1"}]. 342pwd(Config0) -> 343 Config = set_state([reset], Config0), 344 Pid = proplists:get_value(ftp, Config), 345 {ok, PWD} = ftp:pwd(Pid), 346 {ok, PathLpwd} = ftp:lpwd(Pid), 347 PWD = id2ftp_result("", Config), 348 PathLpwd = id2ftp_result("", Config). 349 350%%------------------------------------------------------------------------- 351cd() -> 352 ["Open an ftp connection, log on as anonymous ftp, and cd to a" 353 "directory and to a non-existent directory."]. 354cd(Config0) -> 355 Dir = "test", 356 Config = set_state([reset,{mkdir,Dir}], Config0), 357 Pid = proplists:get_value(ftp, Config), 358 ok = ftp:cd(Pid, id2ftp(Dir,Config)), 359 {ok, PWD} = ftp:pwd(Pid), 360 ExpectedPWD = id2ftp_result(Dir, Config), 361 PWD = ExpectedPWD, 362 {error, epath} = ftp:cd(Pid, ?BAD_DIR), 363 ok. 364 365%%------------------------------------------------------------------------- 366lcd() -> 367 [{doc, "Test api function ftp:lcd/2"}]. 368lcd(Config0) -> 369 Dir = "test", 370 Config = set_state([reset,{mkdir,Dir}], Config0), 371 Pid = proplists:get_value(ftp, Config), 372 ok = ftp:lcd(Pid, id2ftp(Dir,Config)), 373 {ok, PWD} = ftp:lpwd(Pid), 374 ExpectedPWD = id2ftp_result(Dir, Config), 375 PWD = ExpectedPWD, 376 {error, epath} = ftp:lcd(Pid, ?BAD_DIR). 377 378%%------------------------------------------------------------------------- 379ls() -> 380 [{doc, "Open an ftp connection; ls the current directory, and the " 381 "\"test\" directory. We assume that ls never fails, since " 382 "it's output is meant to be read by humans. "}]. 383ls(Config0) -> 384 Config = set_state([reset,{mkdir,"test"}], Config0), 385 Pid = proplists:get_value(ftp, Config), 386 {ok, _R1} = ftp:ls(Pid), 387 {ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)), 388 %% neither nlist nor ls operates on a directory 389 %% they operate on a pathname, which *can* be a 390 %% directory, but can also be a filename or a group 391 %% of files (including wildcards). 392 case proplists:get_value(wildcard_support, Config) of 393 true -> 394 {ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config)); 395 _ -> 396 ok 397 end. 398 399%%------------------------------------------------------------------------- 400nlist() -> 401 [{doc,"Open an ftp connection; nlist the current directory, and the " 402 "\"test\" directory. Nlist does not behave consistenly over " 403 "operating systems. On some it is an error to have an empty " 404 "directory."}]. 405nlist(Config0) -> 406 Config = set_state([reset,{mkdir,"test"}], Config0), 407 Pid = proplists:get_value(ftp, Config), 408 {ok, _R1} = ftp:nlist(Pid), 409 {ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)), 410 %% neither nlist nor ls operates on a directory 411 %% they operate on a pathname, which *can* be a 412 %% directory, but can also be a filename or a group 413 %% of files (including wildcards). 414 case proplists:get_value(wildcard_support, Config) of 415 true -> 416 {ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config)); 417 _ -> 418 ok 419 end. 420 421%%------------------------------------------------------------------------- 422rename() -> 423 [{doc, "Rename a file."}]. 424rename(Config0) -> 425 Contents = <<"ftp_SUITE test ...">>, 426 OldFile = "old.txt", 427 NewFile = "new.txt", 428 Config = set_state([reset,{mkfile,OldFile,Contents}], Config0), 429 Pid = proplists:get_value(ftp, Config), 430 431 ok = ftp:rename(Pid, 432 id2ftp(OldFile,Config), 433 id2ftp(NewFile,Config)), 434 435 true = (chk_file(NewFile,Contents,Config) 436 and chk_no_file([OldFile],Config)), 437 {error,epath} = ftp:rename(Pid, 438 id2ftp("non_existing_file",Config), 439 id2ftp(NewFile,Config)), 440 ok. 441 442%%------------------------------------------------------------------------- 443send() -> 444 [{doc, "Transfer a file with ftp using send/2."}]. 445send(Config0) -> 446 Contents = <<"ftp_SUITE test ...">>, 447 SrcDir = "data", 448 File = "file.txt", 449 Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0), 450 Pid = proplists:get_value(ftp, Config), 451 452 chk_no_file([File],Config), 453 chk_file([SrcDir,File],Contents,Config), 454 455 ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)), 456 ok = ftp:cd(Pid, id2ftp("",Config)), 457 ok = ftp:send(Pid, File), 458 chk_file(File, Contents, Config), 459 460 {error,epath} = ftp:send(Pid, "non_existing_file"), 461 ok. 462 463%%------------------------------------------------------------------------- 464send_3() -> 465 [{doc, "Transfer a file with ftp using send/3."}]. 466send_3(Config0) -> 467 Contents = <<"ftp_SUITE test ...">>, 468 Dir = "incoming", 469 File = "file.txt", 470 RemoteFile = "remfile.txt", 471 Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0), 472 Pid = proplists:get_value(ftp, Config), 473 474 ok = ftp:cd(Pid, id2ftp(Dir,Config)), 475 ok = ftp:lcd(Pid, id2ftp("",Config)), 476 ok = ftp:send(Pid, File, RemoteFile), 477 chk_file([Dir,RemoteFile], Contents, Config), 478 479 {error,epath} = ftp:send(Pid, "non_existing_file", RemoteFile), 480 ok. 481 482%%------------------------------------------------------------------------- 483send_bin() -> 484 [{doc, "Send a binary."}]. 485send_bin(Config0) -> 486 BinContents = <<"ftp_SUITE test ...">>, 487 File = "file.txt", 488 Config = set_state([reset], Config0), 489 Pid = proplists:get_value(ftp, Config), 490 {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)), 491 ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)), 492 chk_file(File, BinContents, Config), 493 {error, efnamena} = ftp:send_bin(Pid, BinContents, "/nothere"), 494 ok. 495 496%%------------------------------------------------------------------------- 497send_chunk() -> 498 [{doc, "Send a binary using chunks."}]. 499send_chunk(Config0) -> 500 Contents1 = <<"1: ftp_SUITE test ...">>, 501 Contents2 = <<"2: ftp_SUITE test ...">>, 502 File = "file.txt", 503 Config = set_state([reset,{mkdir,"incoming"}], Config0), 504 Pid = proplists:get_value(ftp, Config), 505 506 ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)), 507 {error, echunk} = ftp:send_chunk_start(Pid, id2ftp(File,Config)), 508 {error, echunk} = ftp:cd(Pid, "incoming"), 509 {error, enotbinary} = ftp:send_chunk(Pid, "some string"), 510 ok = ftp:send_chunk(Pid, Contents1), 511 ok = ftp:send_chunk(Pid, Contents2), 512 ok = ftp:send_chunk_end(Pid), 513 chk_file(File, <<Contents1/binary,Contents2/binary>>, Config), 514 515 {error, echunk} = ftp:send_chunk(Pid, Contents1), 516 {error, echunk} = ftp:send_chunk_end(Pid), 517 {error, efnamena} = ftp:send_chunk_start(Pid, "/"), 518 ok. 519 520%%------------------------------------------------------------------------- 521delete() -> 522 [{doc, "Delete a file."}]. 523delete(Config0) -> 524 Contents = <<"ftp_SUITE test ...">>, 525 File = "file.txt", 526 Config = set_state([reset,{mkfile,File,Contents}], Config0), 527 Pid = proplists:get_value(ftp, Config), 528 ok = ftp:delete(Pid, id2ftp(File,Config)), 529 chk_no_file([File], Config), 530 {error,epath} = ftp:delete(Pid, id2ftp(File,Config)), 531 ok. 532 533%%------------------------------------------------------------------------- 534mkdir() -> 535 [{doc, "Make a remote directory."}]. 536mkdir(Config0) -> 537 NewDir = "new_dir", 538 Config = set_state([reset], Config0), 539 Pid = proplists:get_value(ftp, Config), 540 ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)), 541 chk_dir([NewDir], Config), 542 {error,epath} = ftp:mkdir(Pid, id2ftp(NewDir,Config)), 543 ok. 544 545%%------------------------------------------------------------------------- 546rmdir() -> 547 [{doc, "Remove a directory."}]. 548rmdir(Config0) -> 549 Dir = "dir", 550 Config = set_state([reset,{mkdir,Dir}], Config0), 551 Pid = proplists:get_value(ftp, Config), 552 ok = ftp:rmdir(Pid, id2ftp(Dir,Config)), 553 chk_no_dir([Dir], Config), 554 {error,epath} = ftp:rmdir(Pid, id2ftp(Dir,Config)), 555 ok. 556 557%%------------------------------------------------------------------------- 558append() -> 559 [{doc, "Append a local file twice to a remote file"}]. 560append(Config0) -> 561 SrcFile = "f_src.txt", 562 DstFile = "f_dst.txt", 563 Contents = <<"ftp_SUITE test ...">>, 564 Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0), 565 Pid = proplists:get_value(ftp, Config), 566 ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), 567 ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), 568 chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config), 569 {error,epath} = ftp:append(Pid, id2ftp("non_existing_file",Config), id2ftp(DstFile,Config)), 570 ok. 571 572%%------------------------------------------------------------------------- 573append_bin() -> 574 [{doc, "Append a local file twice to a remote file using append_bin"}]. 575append_bin(Config0) -> 576 DstFile = "f_dst.txt", 577 Contents = <<"ftp_SUITE test ...">>, 578 Config = set_state([reset], Config0), 579 Pid = proplists:get_value(ftp, Config), 580 ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)), 581 ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)), 582 chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config). 583 584%%------------------------------------------------------------------------- 585append_chunk() -> 586 [{doc, "Append chunks."}]. 587append_chunk(Config0) -> 588 File = "f_dst.txt", 589 Contents = [<<"ER">>,<<"LE">>,<<"RL">>], 590 Config = set_state([reset], Config0), 591 Pid = proplists:get_value(ftp, Config), 592 ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)), 593 {error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))), 594 ok = ftp:append_chunk(Pid,lists:nth(1,Contents)), 595 ok = ftp:append_chunk(Pid,lists:nth(2,Contents)), 596 ok = ftp:append_chunk(Pid,lists:nth(3,Contents)), 597 ok = ftp:append_chunk_end(Pid), 598 chk_file(File, <<"ERLERL">>, Config). 599 600%%------------------------------------------------------------------------- 601recv() -> 602 [{doc, "Receive a file using recv/2"}]. 603recv(Config0) -> 604 File1 = "f_dst1.txt", 605 File2 = "f_dst2.txt", 606 SrcDir = "a_dir", 607 Contents1 = <<"1 ftp_SUITE test ...">>, 608 Contents2 = <<"2 ftp_SUITE test ...">>, 609 Config = set_state([reset, {mkfile,[SrcDir,File1],Contents1}, {mkfile,[SrcDir,File2],Contents2}], Config0), 610 Pid = proplists:get_value(ftp, Config), 611 ok = ftp:cd(Pid, id2ftp(SrcDir,Config)), 612 ok = ftp:lcd(Pid, id2ftp("",Config)), 613 ok = ftp:recv(Pid, File1), 614 chk_file(File1, Contents1, Config), 615 ok = ftp:recv(Pid, File2), 616 chk_file(File2, Contents2, Config), 617 {error,epath} = ftp:recv(Pid, "non_existing_file"), 618 ok. 619 620%%------------------------------------------------------------------------- 621recv_3() -> 622 [{doc,"Receive a file using recv/3"}]. 623recv_3(Config0) -> 624 DstFile = "f_src.txt", 625 SrcFile = "f_dst.txt", 626 Contents = <<"ftp_SUITE test ...">>, 627 Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0), 628 Pid = proplists:get_value(ftp, Config), 629 ok = ftp:cd(Pid, id2ftp("",Config)), 630 ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)), 631 chk_file(DstFile, Contents, Config). 632 633%%------------------------------------------------------------------------- 634recv_bin() -> 635 [{doc, "Receive a file as a binary."}]. 636recv_bin(Config0) -> 637 File = "f_dst.txt", 638 Contents = <<"ftp_SUITE test ...">>, 639 Config = set_state([reset, {mkfile,File,Contents}], Config0), 640 Pid = proplists:get_value(ftp, Config), 641 {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)), 642 find_diff(Received, Contents), 643 {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)), 644 ok. 645 646%%------------------------------------------------------------------------- 647recv_bin_twice() -> 648 [{doc, "Receive two files as a binaries."}]. 649recv_bin_twice(Config0) -> 650 File1 = "f_dst1.txt", 651 File2 = "f_dst2.txt", 652 Contents1 = <<"1 ftp_SUITE test ...">>, 653 Contents2 = <<"2 ftp_SUITE test ...">>, 654 Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0), 655 ct:log("First transfer",[]), 656 Pid = proplists:get_value(ftp, Config), 657 {ok,Received1} = ftp:recv_bin(Pid, id2ftp(File1,Config)), 658 find_diff(Received1, Contents1), 659 ct:log("Second transfer",[]), 660 {ok,Received2} = ftp:recv_bin(Pid, id2ftp(File2,Config)), 661 find_diff(Received2, Contents2), 662 ct:log("Transfers ready!",[]), 663 {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)), 664 ok. 665%%------------------------------------------------------------------------- 666recv_chunk() -> 667 [{doc, "Receive a file using chunk-wise."}]. 668recv_chunk(Config0) -> 669 File = "big_file.txt", 670 Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ), 671 Config = set_state([reset, {mkfile,File,Contents}], Config0), 672 Pid = proplists:get_value(ftp, Config), 673 {error, "ftp:recv_chunk_start/2 not called"} = do_recv_chunk(Pid), 674 ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)), 675 {ok, ReceivedContents} = do_recv_chunk(Pid), 676 find_diff(ReceivedContents, Contents). 677 678recv_chunk_twice() -> 679 [{doc, "Receive two files using chunk-wise."}]. 680recv_chunk_twice(Config0) -> 681 File1 = "big_file1.txt", 682 File2 = "big_file2.txt", 683 Contents1 = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ), 684 Contents2 = crypto:strong_rand_bytes(1200), 685 Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0), 686 Pid = proplists:get_value(ftp, Config), 687 {error, "ftp:recv_chunk_start/2 not called"} = do_recv_chunk(Pid), 688 ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)), 689 {ok, ReceivedContents1} = do_recv_chunk(Pid), 690 ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)), 691 {ok, ReceivedContents2} = do_recv_chunk(Pid), 692 find_diff(ReceivedContents1, Contents1), 693 find_diff(ReceivedContents2, Contents2). 694 695recv_chunk_three_times() -> 696 [{doc, "Receive two files using chunk-wise."}, 697 {timetrap,{seconds,120}}]. 698recv_chunk_three_times(Config0) -> 699 File1 = "big_file1.txt", 700 File2 = "big_file2.txt", 701 File3 = "big_file3.txt", 702 Contents1 = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ), 703 Contents2 = crypto:strong_rand_bytes(1200), 704 Contents3 = list_to_binary( lists:duplicate(1000, lists:seq(255,0,-1)) ), 705 706 Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}, {mkfile,File3,Contents3}], Config0), 707 Pid = proplists:get_value(ftp, Config), 708 {error, "ftp:recv_chunk_start/2 not called"} = do_recv_chunk(Pid), 709 710 ok = ftp:recv_chunk_start(Pid, id2ftp(File3,Config)), 711 {ok, ReceivedContents3} = do_recv_chunk(Pid), 712 713 ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)), 714 {ok, ReceivedContents1} = do_recv_chunk(Pid), 715 716 ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)), 717 {ok, ReceivedContents2} = do_recv_chunk(Pid), 718 719 find_diff(ReceivedContents1, Contents1), 720 find_diff(ReceivedContents2, Contents2), 721 find_diff(ReceivedContents3, Contents3). 722 723 724do_recv_chunk(Pid) -> 725 recv_chunk(Pid, <<>>). 726recv_chunk(Pid, Acc) -> 727 case ftp:recv_chunk(Pid) of 728 ok -> 729 {ok, Acc}; 730 {ok, Bin} -> 731 recv_chunk(Pid, <<Acc/binary, Bin/binary>>); 732 Error -> 733 Error 734 end. 735 736recv_chunk_delay(Config0) when is_list(Config0) -> 737 File1 = "big_file1.txt", 738 Contents = list_to_binary(lists:duplicate(1000, lists:seq(0,255))), 739 Config = set_state([reset, {mkfile,File1,Contents}], Config0), 740 Pid = proplists:get_value(ftp, Config), 741 ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)), 742 {ok, ReceivedContents} = delay_recv_chunk(Pid), 743 find_diff(ReceivedContents, Contents). 744 745delay_recv_chunk(Pid) -> 746 delay_recv_chunk(Pid, <<>>). 747delay_recv_chunk(Pid, Acc) -> 748 ct:pal("Recived size ~p", [byte_size(Acc)]), 749 case ftp:recv_chunk(Pid) of 750 ok -> 751 {ok, Acc}; 752 {ok, Bin} -> 753 ct:sleep(100), 754 delay_recv_chunk(Pid, <<Acc/binary, Bin/binary>>); 755 Error -> 756 Error 757 end. 758 759%%------------------------------------------------------------------------- 760type() -> 761 [{doc,"Test that we can change btween ASCCI and binary transfer mode"}]. 762type(Config) -> 763 Pid = proplists:get_value(ftp, Config), 764 ok = ftp:type(Pid, ascii), 765 ok = ftp:type(Pid, binary), 766 ok = ftp:type(Pid, ascii), 767 {error, etype} = ftp:type(Pid, foobar). 768 769%%------------------------------------------------------------------------- 770quote(Config) -> 771 Pid = proplists:get_value(ftp, Config), 772 ["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257 773 [_| _] = ftp:quote(Pid, "help"), 774 %% This negativ test causes some ftp servers to hang. This test 775 %% is not important for the client, so we skip it for now. 776 %%["425 Can't build data connection: Connection refused."] 777 %% = ftp:quote(Pid, "list"), 778 ok. 779 780%%------------------------------------------------------------------------- 781progress_report_send() -> 782 [{doc, "Test the option progress for ftp:send/[2,3]"}]. 783progress_report_send(Config) when is_list(Config) -> 784 ReportPid = 785 spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), 786 send(Config), 787 receive 788 {ReportPid, ok} -> 789 ok 790 end. 791 792%%------------------------------------------------------------------------- 793progress_report_recv() -> 794 [{doc, "Test the option progress for ftp:recv/[2,3]"}]. 795progress_report_recv(Config) when is_list(Config) -> 796 ReportPid = 797 spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), 798 recv(Config), 799 receive 800 {ReportPid, ok} -> 801 ok 802 end. 803 804%%------------------------------------------------------------------------- 805 806not_owner() -> 807 [{doc, "Test what happens if a process that not owns the connection tries " 808 "to use it"}]. 809not_owner(Config) when is_list(Config) -> 810 Pid = proplists:get_value(ftp, Config), 811 812 Parent = self(), 813 OtherPid = spawn_link( 814 fun() -> 815 {error, not_connection_owner} = ftp:pwd(Pid), 816 ftp:close(Pid), 817 Parent ! {self(), ok} 818 end), 819 receive 820 {OtherPid, ok} -> 821 {ok, _} = ftp:pwd(Pid) 822 end. 823 824 825%%------------------------------------------------------------------------- 826 827 828unexpected_call()-> 829 [{doc, "Test that behaviour of the ftp process if the api is abused"}]. 830unexpected_call(Config) when is_list(Config) -> 831 Flag = process_flag(trap_exit, true), 832 Pid = proplists:get_value(ftp, Config), 833 834 %% Serious programming fault, connetion will be shut down 835 case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of 836 {error, {connection_terminated, 'API_violation'}} -> 837 ok; 838 Unexpected1 -> 839 exit({unexpected_result, Unexpected1}) 840 end, 841 ct:sleep(500), 842 undefined = process_info(Pid, status), 843 process_flag(trap_exit, Flag). 844%%------------------------------------------------------------------------- 845 846unexpected_cast()-> 847 [{doc, "Test that behaviour of the ftp process if the api is abused"}]. 848unexpected_cast(Config) when is_list(Config) -> 849 Flag = process_flag(trap_exit, true), 850 Pid = proplists:get_value(ftp, Config), 851 %% Serious programming fault, connetion will be shut down 852 gen_server:cast(Pid, {self(), foobar, 10}), 853 ct:sleep(500), 854 undefined = process_info(Pid, status), 855 process_flag(trap_exit, Flag). 856%%------------------------------------------------------------------------- 857 858unexpected_bang()-> 859 [{doc, "Test that connection ignores unexpected bang"}]. 860unexpected_bang(Config) when is_list(Config) -> 861 Flag = process_flag(trap_exit, true), 862 Pid = proplists:get_value(ftp, Config), 863 %% Could be an innocent misstake the connection lives. 864 Pid ! foobar, 865 ct:sleep(500), 866 {status, _} = process_info(Pid, status), 867 process_flag(trap_exit, Flag). 868 869%%------------------------------------------------------------------------- 870 871clean_shutdown() -> 872 [{doc, "Test that owning process that exits with reason " 873 "'shutdown' does not cause an error message. OTP 6035"}]. 874 875clean_shutdown(Config) -> 876 Parent = self(), 877 HelperPid = spawn( 878 fun() -> 879 ftp__open(Config, [verbose]), 880 Parent ! ok, 881 receive 882 nothing -> ok 883 end 884 end), 885 receive 886 ok -> 887 PrivDir = proplists:get_value(priv_dir, Config), 888 LogFile = filename:join([PrivDir,"ticket_6035.log"]), 889 error_logger:logfile({open, LogFile}), 890 exit(HelperPid, shutdown), 891 timer:sleep(2000), 892 error_logger:logfile(close), 893 case is_error_report_6035(LogFile) of 894 true -> ok; 895 false -> {fail, "Bad logfile"} 896 end 897 end. 898 899%%------------------------------------------------------------------------- 900start_ftp() -> 901 [{doc, "Start/stop of ftp service"}]. 902start_ftp(Config) -> 903 Pid0 = proplists:get_value(ftp,Config), 904 Pids0 = [ServicePid || {_, ServicePid} <- ftp:services()], 905 true = lists:member(Pid0, Pids0), 906 {ok, [_|_]} = ftp:service_info(Pid0), 907 ftp:stop_service(Pid0), 908 ct:sleep(100), 909 Pids1 = [ServicePid || {_, ServicePid} <- ftp:services()], 910 false = lists:member(Pid0, Pids1), 911 912 Host = proplists:get_value(ftpd_host,Config), 913 Port = proplists:get_value(ftpd_port,Config), 914 915 {ok, Pid1} = ftp:start_standalone([{host, Host},{port, Port}]), 916 Pids2 = [ServicePid || {_, ServicePid} <- ftp:services()], 917 false = lists:member(Pid1, Pids2). 918 919%%------------------------------------------------------------------------- 920ftp_worker() -> 921 [{doc, "Makes sure the ftp worker processes are added and removed " 922 "appropriatly to/from the supervison tree."}]. 923ftp_worker(Config) -> 924 Pid = proplists:get_value(ftp,Config), 925 case supervisor:which_children(ftp_sup) of 926 [{_,_, worker, [ftp]}] -> 927 ftp:stop_service(Pid), 928 ct:sleep(5000), 929 [] = supervisor:which_children(ftp_sup), 930 ok; 931 Children -> 932 ct:fail("Unexpected children: ~p",[Children]) 933 end. 934 935 936%%%---------------------------------------------------------------- 937%%% Error codes not tested elsewhere 938 939error_elogin(Config0) -> 940 Dir = "test", 941 OldFile = "old.txt", 942 NewFile = "new.txt", 943 SrcDir = "data", 944 File = "file.txt", 945 Config = set_state([reset, 946 {mkdir,Dir}, 947 {mkfile,OldFile,<<"Contents..">>}, 948 {mkfile,[SrcDir,File],<<"Contents..">>}], Config0), 949 950 Pid = proplists:get_value(ftp, Config), 951 ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)), 952 {error,elogin} = ftp:send(Pid, File), 953 ok = ftp:lcd(Pid, id2ftp("",Config)), 954 {error,elogin} = ftp:pwd(Pid), 955 {error,elogin} = ftp:cd(Pid, id2ftp(Dir,Config)), 956 {error,elogin} = ftp:rename(Pid, 957 id2ftp(OldFile,Config), 958 id2ftp(NewFile,Config)), 959 ok. 960 961error_ehost(_Config) -> 962 {error, ehost} = ftp:open("nohost.nodomain"), 963 ok. 964 965%%-------------------------------------------------------------------- 966%% Internal functions ----------------------------------------------- 967%%-------------------------------------------------------------------- 968 969chk_file(Path=[C|_], ExpectedContents, Config) when 0<C,C=<255 -> 970 chk_file([Path], ExpectedContents, Config); 971 972chk_file(PathList, ExpectedContents, Config) -> 973 Path = filename:join(PathList), 974 AbsPath = id2abs(Path,Config), 975 case file:read_file(AbsPath) of 976 {ok,ExpectedContents} -> 977 true; 978 {ok,ReadContents} -> 979 {error,{diff,Pos,RC,LC}} = find_diff(ReadContents, ExpectedContents, 1), 980 ct:log("Bad contents of ~p.~nGot:~n~p~nExpected:~n~p~nDiff at pos ~p ~nRead: ~p~nExp : ~p", 981 [AbsPath,ReadContents,ExpectedContents,Pos,RC,LC]), 982 ct:fail("Bad contents of ~p", [Path]); 983 {error,Error} -> 984 try begin 985 {ok,CWD} = file:get_cwd(), 986 ct:log("file:get_cwd()=~p~nfiles:~n~p",[CWD,file:list_dir(CWD)]) 987 end 988 of _ -> ok 989 catch _:_ ->ok 990 end, 991 ct:fail("Error reading ~p: ~p",[Path,Error]) 992 end. 993 994 995chk_no_file(Path=[C|_], Config) when 0<C,C=<255 -> 996 chk_no_file([Path], Config); 997 998chk_no_file(PathList, Config) -> 999 Path = filename:join(PathList), 1000 AbsPath = id2abs(Path,Config), 1001 case file:read_file(AbsPath) of 1002 {error,enoent} -> 1003 true; 1004 {ok,Contents} -> 1005 ct:log("File ~p exists although it shouldn't. Contents:~n~p", 1006 [AbsPath,Contents]), 1007 ct:fail("File exists: ~p", [Path]); 1008 {error,Error} -> 1009 ct:fail("Unexpected error reading ~p: ~p",[Path,Error]) 1010 end. 1011 1012 1013chk_dir(Path=[C|_], Config) when 0<C,C=<255 -> 1014 chk_dir([Path], Config); 1015 1016chk_dir(PathList, Config) -> 1017 Path = filename:join(PathList), 1018 AbsPath = id2abs(Path,Config), 1019 case file:read_file_info(AbsPath) of 1020 {ok, #file_info{type=directory}} -> 1021 true; 1022 {ok, #file_info{type=Type}} -> 1023 ct:fail("Expected dir ~p is a ~p",[Path,Type]); 1024 {error,Error} -> 1025 ct:fail("Expected dir ~p: ~p",[Path,Error]) 1026 end. 1027 1028chk_no_dir(PathList, Config) -> 1029 Path = filename:join(PathList), 1030 AbsPath = id2abs(Path,Config), 1031 case file:read_file_info(AbsPath) of 1032 {error,enoent} -> 1033 true; 1034 {ok, #file_info{type=directory}} -> 1035 ct:fail("Dir ~p erroneously exists",[Path]); 1036 {ok, #file_info{type=Type}} -> 1037 ct:fail("~p ~p erroneously exists",[Type,Path]); 1038 {error,Error} -> 1039 ct:fail("Unexpected error for ~p: ~p",[Path,Error]) 1040 end. 1041 1042%%-------------------------------------------------------------------- 1043find_executable(Config) -> 1044 search_executable(proplists:get_value(ftpservers, Config, ?default_ftp_servers)). 1045 1046 1047search_executable([{Name,Paths,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}|Srvrs]) -> 1048 case os_find(Name,Paths) of 1049 false -> 1050 ct:log("~p not found",[Name]), 1051 search_executable(Srvrs); 1052 AbsName -> 1053 ct:comment("Found ~p",[AbsName]), 1054 {ok, {AbsName,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}} 1055 end; 1056search_executable([]) -> 1057 false. 1058 1059 1060os_find(Name, Paths) -> 1061 case os:find_executable(Name, Paths) of 1062 false -> os:find_executable(Name); 1063 AbsName -> AbsName 1064 end. 1065 1066%%%---------------------------------------------------------------- 1067start_ftpd(Config0) -> 1068 {AbsName,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = 1069 proplists:get_value(ftpd_data, Config0), 1070 case StartCmd(Config0, AbsName) of 1071 {ok,StartResult} -> 1072 Config = [{ftpd_host,Host}, 1073 {ftpd_port,Port}, 1074 {ftpd_start_result,StartResult} | ConfigRewrite(Config0)], 1075 try 1076 ftp__close(ftp__open(Config,[verbose])) 1077 of 1078 Config1 when is_list(Config1) -> 1079 ct:log("Usuable ftp server ~p started on ~p:~p",[AbsName,Host,Port]), 1080 Config 1081 catch 1082 Class:Exception -> 1083 ct:log("Ftp server ~p started on ~p:~p but is unusable:~n~p:~p", 1084 [AbsName,Host,Port,Class,Exception]), 1085 {skip, [AbsName," started but unusuable"]} 1086 end; 1087 {error,Msg} -> 1088 {skip, [AbsName," not started: ",Msg]} 1089 end. 1090 1091stop_ftpd(Config) -> 1092 {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config), 1093 StopCommand(proplists:get_value(ftpd_start_result,Config)). 1094 1095ps_ftpd(Config) -> 1096 {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config), 1097 ct:log( ChkUp(proplists:get_value(ftpd_start_result,Config)) ). 1098 1099 1100ftpd_running(Config) -> 1101 {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config), 1102 ChkUp(proplists:get_value(ftpd_start_result,Config)). 1103 1104ftp__open(Config, Options) -> 1105 Host = proplists:get_value(ftpd_host,Config), 1106 Port = proplists:get_value(ftpd_port,Config), 1107 ct:log("Host=~p, Port=~p",[Host,Port]), 1108 {ok,Pid} = ftp:open(Host, [{port,Port} | Options]), 1109 [{ftp,Pid}|Config]. 1110 1111ftp__close(Config) -> 1112 ok = ftp:close(proplists:get_value(ftp,Config)), 1113 Config. 1114 1115ftp_start_service(Config, Options) -> 1116 Host = proplists:get_value(ftpd_host,Config), 1117 Port = proplists:get_value(ftpd_port,Config), 1118 ct:log("Host=~p, Port=~p",[Host,Port]), 1119 {ok,Pid} = ftp:start_service([{host, Host},{port,Port} | Options]), 1120 [{ftp,Pid}|Config]. 1121 1122ftp_stop_service(Config) -> 1123 ok = ftp:stop_service(proplists:get_value(ftp,Config)), 1124 Config. 1125 1126split(Cs) -> string:tokens(Cs, "\r\n"). 1127 1128find_diff(Bin1, Bin2) -> 1129 case find_diff(Bin1, Bin2, 1) of 1130 {error, {diff,Pos,RC,LC}} -> 1131 ct:log("Contents differ at position ~p.~nOp1: ~p~nOp2: ~p",[Pos,RC,LC]), 1132 ct:fail("Contents differ at pos ~p",[Pos]); 1133 Other -> 1134 Other 1135 end. 1136 1137find_diff(A, A, _) -> true; 1138find_diff(<<H,T1/binary>>, <<H,T2/binary>>, Pos) -> find_diff(T1, T2, Pos+1); 1139find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}. 1140 1141set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops); 1142 1143set_state(reset, Config) -> 1144 rm('*', id2abs("",Config)), 1145 PrivDir = proplists:get_value(priv_dir,Config), 1146 file:set_cwd(PrivDir), 1147 ftp:lcd(proplists:get_value(ftp,Config),PrivDir), 1148 set_state({mkdir,""},Config); 1149set_state({mkdir,Id}, Config) -> 1150 Abs = id2abs(Id, Config), 1151 mk_path(Abs), 1152 file:make_dir(Abs), 1153 Config; 1154set_state({mkfile,Id,Contents}, Config) -> 1155 Abs = id2abs(Id, Config), 1156 mk_path(Abs), 1157 ok = file:write_file(Abs, Contents), 1158 Config. 1159 1160mk_path(Abs) -> lists:foldl(fun mk_path/2, [], filename:split(filename:dirname(Abs))). 1161 1162mk_path(F, Pfx) -> 1163 case file:read_file_info(AbsName=filename:join(Pfx,F)) of 1164 {ok,#file_info{type=directory}} -> 1165 AbsName; 1166 {error,eexist} -> 1167 AbsName; 1168 {error,enoent} -> 1169 ok = file:make_dir(AbsName), 1170 AbsName 1171 end. 1172 1173rm('*', Pfx) -> 1174 {ok,Fs} = file:list_dir(Pfx), 1175 lists:foreach(fun(F) -> rm(F, Pfx) end, Fs); 1176rm(F, Pfx) -> 1177 case file:read_file_info(AbsName=filename:join(Pfx,F)) of 1178 {ok,#file_info{type=directory}} -> 1179 {ok,Fs} = file:list_dir(AbsName), 1180 lists:foreach(fun(F1) -> rm(F1,AbsName) end, Fs), 1181 ok = file:del_dir(AbsName); 1182 1183 {ok,#file_info{type=regular}} -> 1184 ok = file:delete(AbsName); 1185 1186 {error,enoent} -> 1187 ok 1188 end. 1189 1190id2abs(Id, Conf) -> filename:join(proplists:get_value(priv_dir,Conf),ids(Id)). 1191id2ftp(Id, Conf) -> (proplists:get_value(id2ftp,Conf))(ids(Id)). 1192id2ftp_result(Id, Conf) -> (proplists:get_value(id2ftp_result,Conf))(ids(Id)). 1193 1194ids([[_|_]|_]=Ids) -> filename:join(Ids); 1195ids(Id) -> Id. 1196 1197 1198is_expected_absName(Id, File, Conf) -> File = (proplists:get_value(id2abs,Conf))(Id). 1199is_expected_ftpInName(Id, File, Conf) -> File = (proplists:get_value(id2ftp,Conf))(Id). 1200is_expected_ftpOutName(Id, File, Conf) -> File = (proplists:get_value(id2ftp_result,Conf))(Id). 1201 1202 1203%%%---------------------------------------------------------------- 1204%%% Help functions for the option '{progress,Progress}' 1205%%% 1206 1207%%%---------------- 1208%%% Callback: 1209 1210progress(#progress{} = P, _File, {file_size, Total} = M) -> 1211 ct:pal("Progress: ~p",[M]), 1212 progress_report_receiver ! start, 1213 P#progress{total = Total}; 1214 1215progress(#progress{current = Current} = P, _File, {transfer_size, 0} = M) -> 1216 ct:pal("Progress: ~p",[M]), 1217 progress_report_receiver ! finish, 1218 case P#progress.total of 1219 unknown -> P; 1220 Current -> P; 1221 Total -> ct:fail({error, {progress, {total,Total}, {current,Current}}}), 1222 P 1223 end; 1224 1225progress(#progress{current = Current} = P, _File, {transfer_size, Size} = M) -> 1226 ct:pal("Progress: ~p",[M]), 1227 progress_report_receiver ! update, 1228 P#progress{current = Current + Size}; 1229 1230progress(P, _File, M) -> 1231 ct:pal("Progress **** Strange: ~p",[M]), 1232 P. 1233 1234 1235%%%---------------- 1236%%% Help process that counts the files transferred: 1237 1238progress_report_receiver_init(Parent, N) -> 1239 register(progress_report_receiver, self()), 1240 progress_report_receiver_expect_N_files(Parent, N). 1241 1242progress_report_receiver_expect_N_files(_Parent, 0) -> 1243 ct:pal("progress_report got all files!", []); 1244progress_report_receiver_expect_N_files(Parent, N) -> 1245 ct:pal("progress_report expects ~p more files",[N]), 1246 receive 1247 start -> ok 1248 end, 1249 progress_report_receiver_loop(Parent, N-1). 1250 1251 1252progress_report_receiver_loop(Parent, N) -> 1253 ct:pal("progress_report expect update | finish. N = ~p",[N]), 1254 receive 1255 update -> 1256 ct:pal("progress_report got update",[]), 1257 progress_report_receiver_loop(Parent, N); 1258 finish -> 1259 ct:pal("progress_report got finish, send ~p to ~p",[{self(),ok}, Parent]), 1260 Parent ! {self(), ok}, 1261 progress_report_receiver_expect_N_files(Parent, N) 1262 end. 1263 1264%%%---------------------------------------------------------------- 1265%%% Help functions for bug OTP-6035 1266 1267is_error_report_6035(LogFile) -> 1268 case file:read_file(LogFile) of 1269 {ok, Bin} -> 1270 nomatch =/= binary:match(Bin, <<"=ERROR REPORT====">>); 1271 _ -> 1272 false 1273 end. 1274 1275