aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/Makefile2
-rw-r--r--lib/common_test/doc/src/ct_ftp.xml6
-rw-r--r--lib/common_test/src/common_test.app.src1
-rw-r--r--lib/common_test/src/ct_ftp.erl8
-rw-r--r--lib/ftp/AUTHORS11
-rw-r--r--lib/ftp/Makefile78
-rw-r--r--lib/ftp/doc/archive/rfc2428.txt (renamed from lib/inets/doc/archive/rfc2428.txt)0
-rw-r--r--lib/ftp/doc/archive/rfc2577.txt (renamed from lib/inets/doc/archive/rfc2577.txt)0
-rw-r--r--lib/ftp/doc/archive/rfc959.txt (renamed from lib/inets/doc/archive/rfc959.txt)0
-rw-r--r--lib/ftp/doc/html/.gitignore0
-rw-r--r--lib/ftp/doc/man3/.gitignore0
-rw-r--r--lib/ftp/doc/man6/.gitignore0
-rw-r--r--lib/ftp/doc/pdf/.gitignore0
-rw-r--r--lib/ftp/doc/src/Makefile154
-rw-r--r--lib/ftp/doc/src/book.xml49
-rw-r--r--lib/ftp/doc/src/ftp.xml (renamed from lib/inets/doc/src/ftp.xml)51
-rw-r--r--lib/ftp/doc/src/ftp_client.xml (renamed from lib/inets/doc/src/ftp_client.xml)14
-rw-r--r--lib/ftp/doc/src/introduction.xml46
-rw-r--r--lib/ftp/doc/src/notes.xml53
-rw-r--r--lib/ftp/doc/src/part.xml37
-rw-r--r--lib/ftp/doc/src/ref_man.xml36
-rw-r--r--lib/ftp/ebin/.gitignore0
-rw-r--r--lib/ftp/info2
-rw-r--r--lib/ftp/src/Makefile (renamed from lib/inets/src/ftp/Makefile)92
-rw-r--r--lib/ftp/src/ftp.app.src19
-rw-r--r--lib/ftp/src/ftp.appup.src26
-rw-r--r--lib/ftp/src/ftp.erl (renamed from lib/inets/src/ftp/ftp.erl)568
-rw-r--r--lib/ftp/src/ftp_app.erl47
-rw-r--r--lib/ftp/src/ftp_internal.hrl (renamed from lib/inets/src/ftp/ftp_internal.hrl)18
-rw-r--r--lib/ftp/src/ftp_progress.erl (renamed from lib/inets/src/ftp/ftp_progress.erl)0
-rw-r--r--lib/ftp/src/ftp_response.erl (renamed from lib/inets/src/ftp/ftp_response.erl)0
-rw-r--r--lib/ftp/src/ftp_sup.erl68
-rw-r--r--lib/ftp/test/Makefile251
-rw-r--r--lib/ftp/test/erl_make_certs.erl475
-rw-r--r--lib/ftp/test/ftp.config1
-rw-r--r--lib/ftp/test/ftp.cover2
-rw-r--r--lib/ftp/test/ftp.spec1
-rw-r--r--lib/ftp/test/ftp_SUITE.erl (renamed from lib/inets/test/ftp_SUITE.erl)139
-rw-r--r--lib/ftp/test/ftp_SUITE_data/ftpd_hosts.skel (renamed from lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel)0
-rw-r--r--lib/ftp/test/ftp_SUITE_data/vsftpd.conf33
-rw-r--r--lib/ftp/test/ftp_bench.spec1
-rw-r--r--lib/ftp/test/ftp_format_SUITE.erl (renamed from lib/inets/test/ftp_format_SUITE.erl)0
l---------lib/ftp/test/ftp_internal.hrl1
-rw-r--r--lib/ftp/test/ftp_property_test_SUITE.erl (renamed from lib/inets/test/ftp_property_test_SUITE.erl)4
-rw-r--r--lib/ftp/test/ftp_test_lib.erl126
-rw-r--r--lib/ftp/test/property_test/README12
-rw-r--r--lib/ftp/test/property_test/ftp_simple_client_server.erl307
-rw-r--r--lib/ftp/test/property_test/ftp_simple_client_server_data/vsftpd.conf (renamed from lib/inets/test/ftp_SUITE_data/vsftpd.conf)4
-rw-r--r--lib/ftp/vsn.mk24
-rw-r--r--lib/inets/doc/src/Makefile3
-rw-r--r--lib/inets/doc/src/inets.xml7
-rw-r--r--lib/inets/doc/src/introduction.xml10
-rw-r--r--lib/inets/doc/src/notes.xml14
-rw-r--r--lib/inets/doc/src/part.xml9
-rw-r--r--lib/inets/doc/src/ref_man.xml12
-rw-r--r--lib/inets/src/ftp/ftp_sup.erl60
-rw-r--r--lib/inets/src/inets_app/Makefile4
-rw-r--r--lib/inets/src/inets_app/inets.app.src13
-rw-r--r--lib/inets/src/inets_app/inets.erl14
-rw-r--r--lib/inets/src/inets_app/inets_ftp_wrapper.erl48
-rw-r--r--lib/inets/src/inets_app/inets_sup.erl28
-rw-r--r--lib/inets/src/inets_app/inets_tftp_wrapper.erl48
-rw-r--r--lib/inets/src/subdirs.mk2
-rw-r--r--lib/inets/test/Makefile55
l---------lib/inets/test/ftp_internal.hrl1
-rw-r--r--lib/inets/test/inets_SUITE.erl77
-rw-r--r--lib/inets/test/inets_socketwrap_SUITE.erl34
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl52
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml4
-rw-r--r--lib/tftp/AUTHORS11
-rw-r--r--lib/tftp/Makefile78
-rw-r--r--lib/tftp/doc/html/.gitignore0
-rw-r--r--lib/tftp/doc/man3/.gitignore0
-rw-r--r--lib/tftp/doc/man6/.gitignore0
-rw-r--r--lib/tftp/doc/pdf/.gitignore0
-rw-r--r--lib/tftp/doc/src/Makefile154
-rw-r--r--lib/tftp/doc/src/book.xml49
-rw-r--r--lib/tftp/doc/src/getting_started.xml81
-rw-r--r--lib/tftp/doc/src/introduction.xml62
-rw-r--r--lib/tftp/doc/src/notes.xml53
-rw-r--r--lib/tftp/doc/src/ref_man.xml36
-rw-r--r--lib/tftp/doc/src/tftp.xml (renamed from lib/inets/doc/src/tftp.xml)52
-rw-r--r--lib/tftp/doc/src/usersguide.xml37
-rw-r--r--lib/tftp/ebin/.gitignore0
-rw-r--r--lib/tftp/info2
-rw-r--r--lib/tftp/src/Makefile (renamed from lib/inets/src/tftp/Makefile)51
-rw-r--r--lib/tftp/src/tftp.app.src22
-rw-r--r--lib/tftp/src/tftp.appup.src26
-rw-r--r--lib/tftp/src/tftp.erl (renamed from lib/inets/src/tftp/tftp.erl)15
-rw-r--r--lib/tftp/src/tftp.hrl (renamed from lib/inets/src/tftp/tftp.hrl)0
-rw-r--r--lib/tftp/src/tftp_app.erl56
-rw-r--r--lib/tftp/src/tftp_binary.erl (renamed from lib/inets/src/tftp/tftp_binary.erl)0
-rw-r--r--lib/tftp/src/tftp_engine.erl (renamed from lib/inets/src/tftp/tftp_engine.erl)36
-rw-r--r--lib/tftp/src/tftp_file.erl (renamed from lib/inets/src/tftp/tftp_file.erl)12
-rw-r--r--lib/tftp/src/tftp_lib.erl (renamed from lib/inets/src/tftp/tftp_lib.erl)0
-rw-r--r--lib/tftp/src/tftp_logger.erl (renamed from lib/inets/src/tftp/tftp_logger.erl)0
-rw-r--r--lib/tftp/src/tftp_sup.erl (renamed from lib/inets/src/tftp/tftp_sup.erl)2
-rw-r--r--lib/tftp/test/Makefile250
-rw-r--r--lib/tftp/test/tftp.config1
-rw-r--r--lib/tftp/test/tftp.cover2
-rw-r--r--lib/tftp/test/tftp.spec1
-rw-r--r--lib/tftp/test/tftp_SUITE.erl (renamed from lib/inets/test/tftp_SUITE.erl)92
-rw-r--r--lib/tftp/test/tftp_bench.spec1
-rw-r--r--lib/tftp/test/tftp_test_lib.erl (renamed from lib/inets/test/tftp_test_lib.erl)4
-rw-r--r--lib/tftp/test/tftp_test_lib.hrl (renamed from lib/inets/test/tftp_test_lib.hrl)0
-rw-r--r--lib/tftp/vsn.mk24
-rwxr-xr-xscripts/run-dialyzer6
107 files changed, 3612 insertions, 866 deletions
diff --git a/lib/Makefile b/lib/Makefile
index ae466ed518..d67e605875 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -35,7 +35,7 @@ ALL_ERLANG_APPLICATIONS = xmerl edoc erl_docgen snmp otp_mibs erl_interface \
public_key ssl observer odbc diameter \
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco \
- eunit ssh eldap dialyzer hipe
+ eunit ssh eldap dialyzer hipe ftp tftp
ifdef BUILD_ALL
ERLANG_APPLICATIONS += $(ALL_ERLANG_APPLICATIONS)
diff --git a/lib/common_test/doc/src/ct_ftp.xml b/lib/common_test/doc/src/ct_ftp.xml
index e8c6f72db7..a6f01dd58e 100644
--- a/lib/common_test/doc/src/ct_ftp.xml
+++ b/lib/common_test/doc/src/ct_ftp.xml
@@ -33,13 +33,11 @@
<file>ct_ftp.xml</file>
</header>
<module>ct_ftp</module>
- <modulesummary>FTP client module (based on the FTP support of the Inets
- application).</modulesummary>
+ <modulesummary>FTP client module (based on the FTP application).</modulesummary>
<description>
- <p>FTP client module (based on the FTP support of the <c>Inets</c>
- application).</p>
+ <p>FTP client module (based on the <c>ftp</c> application).</p>
</description>
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 9becde110b..f686003637 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -85,6 +85,7 @@
"crypto-3.6",
"debugger-4.1",
"erts-7.0",
+ "ftp-1.0.0",
"inets-6.0",
"kernel-4.0",
"observer-2.1",
diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl
index ee4a6a6c45..8b02ae3a0f 100644
--- a/lib/common_test/src/ct_ftp.erl
+++ b/lib/common_test/src/ct_ftp.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
%%
-%%% @doc FTP client module (based on the FTP support of the INETS application).
+%%% @doc FTP client module (based on the FTP application).
%%%
%%% @type connection() = handle() | ct:target_name()
%%% @type handle() = ct_gen_conn:handle(). Handle for a specific
@@ -292,8 +292,8 @@ init(KeyOrName,{IP,Port},{Username,Password}) ->
end.
ftp_connect(IP,Port,Username,Password) ->
- _ = inets:start(),
- case inets:start(ftpc,[{host,IP},{port,Port}]) of
+ _ = ftp:start(),
+ case ftp:start_service([{host,IP},{port,Port}]) of
{ok,FtpPid} ->
case ftp:user(FtpPid,Username,Password) of
ok ->
@@ -341,7 +341,7 @@ reconnect(_Addr,_State) ->
terminate(FtpPid,State) ->
log(heading(terminate,State#state.target_name),
"Closing FTP connection.\nHandle: ~p\n",[FtpPid]),
- inets:stop(ftpc,FtpPid).
+ ftp:stop_service(FtpPid).
%%%=================================================================
diff --git a/lib/ftp/AUTHORS b/lib/ftp/AUTHORS
new file mode 100644
index 0000000000..88dcbf602a
--- /dev/null
+++ b/lib/ftp/AUTHORS
@@ -0,0 +1,11 @@
+Original Authors:
+
+Peter Högfeldt - first version of ftp
+
+Contributors:
+
+Ingela Anderton Andin
+Martin Gustafsson
+Johan Blom
+Torbjörn Törnkvist
+Joe Armstrong \ No newline at end of file
diff --git a/lib/ftp/Makefile b/lib/ftp/Makefile
new file mode 100644
index 0000000000..555f8b0dea
--- /dev/null
+++ b/lib/ftp/Makefile
@@ -0,0 +1,78 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Macros
+# ----------------------------------------------------
+
+SUB_DIRECTORIES = src doc/src
+
+include vsn.mk
+VSN = $(FTP_VSN)
+
+SPECIAL_TARGETS =
+
+DIA_PLT = ./priv/plt/$(APPLICATION).plt
+DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
+
+
+# ----------------------------------------------------
+# Default Subdir Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_subdir.mk
+
+.PHONY: info gclean dialyzer dialyzer_plt dclean
+
+info:
+ @echo "OS: $(OS)"
+ @echo "DOCB: $(DOCB)"
+ @echo ""
+ @echo "FTP_VSN: $(FTP_VSN)"
+ @echo "APP_VSN: $(APP_VSN)"
+ @echo ""
+ @echo "DIA_PLT: $(DIA_PLT)"
+ @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
+ @echo ""
+
+gclean:
+ git clean -fXd
+
+dclean:
+ rm -f $(DIA_PLT)
+ rm -f $(DIA_ANALYSIS)
+
+dialyzer_plt: $(DIA_PLT)
+
+$(DIA_PLT):
+ @echo "Building $(APPLICATION) plt file"
+ @dialyzer --build_plt \
+ --output_plt $@ \
+ -r ../$(APPLICATION)/ebin \
+ --output $(DIA_ANALYSIS) \
+ --verbose
+
+dialyzer: $(DIA_PLT)
+ @echo "Running dialyzer on $(APPLICATION)"
+ @dialyzer --plt $< \
+ ../$(APPLICATION)/ebin \
+ --verbose
diff --git a/lib/inets/doc/archive/rfc2428.txt b/lib/ftp/doc/archive/rfc2428.txt
index a6ec3535ed..a6ec3535ed 100644
--- a/lib/inets/doc/archive/rfc2428.txt
+++ b/lib/ftp/doc/archive/rfc2428.txt
diff --git a/lib/inets/doc/archive/rfc2577.txt b/lib/ftp/doc/archive/rfc2577.txt
index 83ba203130..83ba203130 100644
--- a/lib/inets/doc/archive/rfc2577.txt
+++ b/lib/ftp/doc/archive/rfc2577.txt
diff --git a/lib/inets/doc/archive/rfc959.txt b/lib/ftp/doc/archive/rfc959.txt
index 5c9f11af5d..5c9f11af5d 100644
--- a/lib/inets/doc/archive/rfc959.txt
+++ b/lib/ftp/doc/archive/rfc959.txt
diff --git a/lib/ftp/doc/html/.gitignore b/lib/ftp/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ftp/doc/html/.gitignore
diff --git a/lib/ftp/doc/man3/.gitignore b/lib/ftp/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ftp/doc/man3/.gitignore
diff --git a/lib/ftp/doc/man6/.gitignore b/lib/ftp/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ftp/doc/man6/.gitignore
diff --git a/lib/ftp/doc/pdf/.gitignore b/lib/ftp/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ftp/doc/pdf/.gitignore
diff --git a/lib/ftp/doc/src/Makefile b/lib/ftp/doc/src/Makefile
new file mode 100644
index 0000000000..e96a9c032f
--- /dev/null
+++ b/lib/ftp/doc/src/Makefile
@@ -0,0 +1,154 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2018. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(FTP_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+
+XML_CHAPTER_FILES = \
+ introduction.xml \
+ ftp_client.xml \
+ notes.xml
+
+XML_REF3_FILES = \
+ ftp.xml
+
+XML_PART_FILES = \
+ part.xml
+
+BOOK_FILES = book.xml
+
+XML_FILES = \
+ $(BOOK_FILES) \
+ $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) \
+ $(XML_REF6_FILES) \
+ $(XML_REF3_FILES) \
+ $(XML_APPLICATION_FILES)
+
+# GIF_FILES = ftp.gif
+
+
+# ----------------------------------------------------
+
+HTML_FILES = \
+ $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+EXTRA_FILES = \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+ldocs: local_docs
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+clean clean_docs: clean_html clean_man clean_pdf
+ rm -f errs core *~
+
+man: $(MAN3_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt:
+
+clean_pdf:
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+
+clean_html:
+ rm -rf $(TOP_HTML_FILES) $(HTMLDIR)/*
+
+clean_man:
+ rm -f $(MAN3_FILES)
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+
+release_spec:
+
+info:
+ @echo "GIF_FILES:\n$(GIF_FILES)"
+ @echo ""
+ @echo "EXTRA_FILES:\n$(EXTRA_FILES)"
+ @echo ""
+ @echo "HTML_FILES:\n$(HTML_FILES)"
+ @echo ""
+ @echo "TOP_HTML_FILES:\n$(TOP_HTML_FILES)"
+ @echo ""
+ @echo "XML_REF3_FILES:\n$(XML_REF3_FILES)"
+ @echo ""
+ @echo "XML_REF6_FILES:\n$(XML_REF6_FILES)"
+ @echo ""
+ @echo "XML_CHAPTER_FILES:\n$(XML_CHAPTER_FILES)"
+ @echo ""
diff --git a/lib/ftp/doc/src/book.xml b/lib/ftp/doc/src/book.xml
new file mode 100644
index 0000000000..1268af64bf
--- /dev/null
+++ b/lib/ftp/doc/src/book.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>1997</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>FTP</title>
+ <prepared>Péter Dimitrov</prepared>
+ <docno></docno>
+ <date>2018-02-26</date>
+ <rev>1.0</rev>
+ <file>book.sgml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>FTP</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
diff --git a/lib/inets/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml
index 42bece4d38..18770ebcb4 100644
--- a/lib/inets/doc/src/ftp.xml
+++ b/lib/ftp/doc/src/ftp.xml
@@ -38,20 +38,19 @@
according to a subset of the File Transfer Protocol (FTP), see
<url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>.</p>
- <p>As from <c>Inets</c> 4.4.1, the FTP
- client always tries to use passive FTP mode and only resort
+ <p>The FTP client always tries to use passive FTP mode and only resort
to active FTP mode if this fails. This default behavior can be
changed by start option <seealso marker="#mode">mode</seealso>.</p>
<marker id="two_start"></marker>
<p>An FTP client can be started in two ways. One is using the
- <seealso marker="#service_start">Inets service framework</seealso>,
- the other is to start it directly as a standalone process
+ <seealso marker="#service_start">service_start</seealso> function,
+ the other is to start it directly as a standalone process
using function <seealso marker="#open">open</seealso>.</p>
<p>For a simple example of an FTP session, see
- <seealso marker="ftp_client">Inets User's Guide</seealso>.</p>
+ <seealso marker="ftp_client">FTP User's Guide</seealso>.</p>
<p>In addition to the ordinary functions for receiving and sending
files (see <c>recv/2</c>, <c>recv/3</c>, <c>send/2</c>, and
@@ -82,11 +81,9 @@
<title>FTP CLIENT SERVICE START/STOP</title>
<p>The FTP client can be started and stopped dynamically in runtime by
- calling the <c>Inets</c> application API
- <c>inets:start(ftpc, ServiceConfig)</c>,
- or <c>inets:start(ftpc, ServiceConfig, How)</c>, and
- <c>inets:stop(ftpc, Pid)</c>.
- For details, see <seealso marker="inets">inets(3)</seealso>.</p>
+ calling the <c>ftp</c> application API
+ <c>ftp:start_service(ServiceConfig)</c> and
+ <c>ftp:stop_service(Pid)</c>.</p>
<p>The available configuration options are as follows:</p>
@@ -273,6 +270,7 @@
</section>
<funcs>
+
<func>
<name>account(Pid, Account) -> ok | {error, Reason}</name>
<fsummary>Specifies which account to use.</fsummary>
@@ -564,7 +562,7 @@
<desc>
<p>Starts a standalone FTP client process
- (without the <c>Inets</c> service framework) and
+ (without the <c>ftp</c> service framework) and
opens a session with the FTP server at <c>Host</c>. </p>
<p>If option <c>{tls, tls_options()}</c> is present, the FTP session
@@ -797,6 +795,37 @@
</func>
<func>
+ <name>start_service(ServiceConfig) -> {ok, Pid} | {error, Reason}</name>
+ <fsummary>Dynamically starts an <c>FTP</c>
+ session after the <c>ftp</c> application has been started.</fsummary>
+ <type>
+ <v>ServiceConfig = [{Option, Value}]</v>
+ <v>Option = property()</v>
+ <v>Value = term()</v>
+ </type>
+ <desc>
+ <p>Dynamically starts an <c>FTP</c> session after the <c>ftp</c>
+ application has been started.</p>
+ <note>
+ <p>As long as the <c>ftp</c> application is operational,
+ the FTP sessions are supervised and can be soft code upgraded.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name>stop_service(Reference) -> ok | {error, Reason} </name>
+ <fsummary>Stops an FTP session.</fsummary>
+ <type>
+ <v>Reference = pid() | term() - service-specified reference</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Stops a started FTP session.</p>
+ </desc>
+ </func>
+
+ <func>
<name>type(Pid, Type) -> ok | {error, Reason}</name>
<fsummary>Sets transfer type to <c>ascii</c>or <c>binary</c>.</fsummary>
<type>
diff --git a/lib/inets/doc/src/ftp_client.xml b/lib/ftp/doc/src/ftp_client.xml
index 990dd68604..047b055be7 100644
--- a/lib/inets/doc/src/ftp_client.xml
+++ b/lib/ftp/doc/src/ftp_client.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -23,7 +23,7 @@
</legalnotice>
<title>FTP Client</title>
- <prepared>Ingela Anderton Andin</prepared>
+ <prepared>Péter Dimitrov</prepared>
<responsible></responsible>
<docno></docno>
<approved></approved>
@@ -53,9 +53,9 @@
the user <c>guest</c> with password <c>password</c> logs on to
the remote host <c>erlang.org</c>:</p>
<code type="erl"><![CDATA[
- 1> inets:start().
+ 1> ftp:start().
ok
- 2> {ok, Pid} = inets:start(ftpc, [{host, "erlang.org"}]).
+ 2> {ok, Pid} = ftp:start_service([{host, "erlang.org"}]).
{ok,<0.22.0>}
3> ftp:user(Pid, "guest", "password").
ok
@@ -69,7 +69,9 @@
ok
8> ftp:recv(Pid, "appl.erl").
ok
- 9> inets:stop(ftpc, Pid).
+ 9> ftp:stop_service(Pid).
+ ok
+ 10> ftp:stop().
ok
]]></code>
<p> The file
@@ -82,5 +84,3 @@
<c>/home/guest/appl/examples</c>.</p>
</section>
</chapter>
-
-
diff --git a/lib/ftp/doc/src/introduction.xml b/lib/ftp/doc/src/introduction.xml
new file mode 100644
index 0000000000..cc3673a0fc
--- /dev/null
+++ b/lib/ftp/doc/src/introduction.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared>Péter Dimitrov</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2018-02-26</date>
+ <rev>A</rev>
+ <file>introduction.xml</file>
+ </header>
+
+ <section>
+ <title>Purpose</title>
+ <p>An <c>FTP</c> client.</p>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang
+ programming language, concepts of OTP, and has a basic
+ understanding of the FTP protocol.</p>
+ </section>
+</chapter>
diff --git a/lib/ftp/doc/src/notes.xml b/lib/ftp/doc/src/notes.xml
new file mode 100644
index 0000000000..50f38941e5
--- /dev/null
+++ b/lib/ftp/doc/src/notes.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2002</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>FTP Release Notes</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2018-02-26</date>
+ <rev>A</rev>
+ <file>notes.xml</file>
+ </header>
+
+ <section><title>FTP 1.0</title>
+
+ <section><title>First released version</title>
+ <list>
+ <item>
+ <p>
+ Inets application was split into multiple smaller protocol specific applications.
+ The FTP application is a standalone FTP client with the same functionality as
+ FTP client in Inets.</p>
+ <p>
+ Own Id: OTP-14113</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+</chapter>
diff --git a/lib/ftp/doc/src/part.xml b/lib/ftp/doc/src/part.xml
new file mode 100644
index 0000000000..ec05f5ac76
--- /dev/null
+++ b/lib/ftp/doc/src/part.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2004</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>FTP User's Guide</title>
+ <prepared>Péter Dimitrov</prepared>
+ <docno></docno>
+ <date>2018-02-26</date>
+ <rev>A</rev>
+ <file>part.sgml</file>
+ </header>
+ <description>
+ <p>The <c>FTP</c> application provides an FTP client.</p>
+ </description>
+ <xi:include href="introduction.xml"/>
+ <xi:include href="ftp_client.xml"/>
+</part>
diff --git a/lib/ftp/doc/src/ref_man.xml b/lib/ftp/doc/src/ref_man.xml
new file mode 100644
index 0000000000..925842610d
--- /dev/null
+++ b/lib/ftp/doc/src/ref_man.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1997</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>FTP Reference Manual</title>
+ <prepared>Péter Dimitrov</prepared>
+ <docno></docno>
+ <date>2018-03-09</date>
+ <rev>1.0</rev>
+ <file>ref_man.xml</file>
+ </header>
+ <description>
+ <p>An <c>FTP</c> client.</p>
+ </description>
+ <xi:include href="ftp.xml"/>
+</application>
diff --git a/lib/ftp/ebin/.gitignore b/lib/ftp/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ftp/ebin/.gitignore
diff --git a/lib/ftp/info b/lib/ftp/info
new file mode 100644
index 0000000000..f6c62d19c2
--- /dev/null
+++ b/lib/ftp/info
@@ -0,0 +1,2 @@
+group: comm
+short: FTP client
diff --git a/lib/inets/src/ftp/Makefile b/lib/ftp/src/Makefile
index 6b99694ea7..6a6df6bde4 100644
--- a/lib/inets/src/ftp/Makefile
+++ b/lib/ftp/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2016. All Rights Reserved.
+# Copyright Ericsson AB 1999-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,88 +17,102 @@
#
# %CopyrightEnd%
#
+
#
include $(ERL_TOP)/make/target.mk
-EBIN = ../../ebin
include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
-include ../../vsn.mk
-
-VSN = $(INETS_VSN)
-
+include ../vsn.mk
+VSN=$(FTP_VSN)
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
+RELSYSDIR = $(RELEASE_PATH)/lib/ftp-$(VSN)
# ----------------------------------------------------
-# Target Specs
+# Common Macros
# ----------------------------------------------------
-MODULES = \
+
+BEHAVIOUR_MODULES=
+
+MODULES= \
ftp \
+ ftp_app \
ftp_progress \
ftp_response \
- ftp_sup
+ ftp_sup
+
+
+INTERNAL_HRL_FILES =
-HRL_FILES = ftp_internal.hrl
+ERL_FILES= \
+ $(MODULES:%=%.erl) \
+ $(BEHAVIOUR_MODULES:%=%.erl)
-ERL_FILES = $(MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+APP_FILE= ftp.app
+APPUP_FILE= ftp.appup
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-
-include ../inets_app/inets.mk
-
-ifeq ($(FTP_DEBUG),true)
- INETS_FLAGS += -Dftp_debug
-endif
-
-ERL_COMPILE_FLAGS += \
- $(INETS_FLAGS) \
- $(INETS_ERL_COMPILE_FLAGS) \
- -I../../include \
- -I../inets_app
+EXTRA_ERLC_FLAGS = +warn_unused_vars
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
+ -pz $(EBIN) \
+ -pz $(ERL_TOP)/lib/public_key/ebin \
+ $(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\"
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-debug opt: $(TARGET_FILES)
+$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
+
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES)
- rm -f core
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
+ rm -f errs core *~
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
docs:
+
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/src"
- $(INSTALL_DIR) "$(RELSYSDIR)/src/ftp"
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/ftp"
- $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
- $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DIR) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \
+ $(APPUP_TARGET) "$(RELSYSDIR)/ebin"
release_docs_spec:
-info:
- @echo "APPLICATION = $(APPLICATION)"
- @echo "INETS_DEBUG = $(INETS_DEBUG)"
- @echo "INETS_FLAGS = $(INETS_FLAGS)"
- @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)"
+# ----------------------------------------------------
+# Dependencies
+# ----------------------------------------------------
+
diff --git a/lib/ftp/src/ftp.app.src b/lib/ftp/src/ftp.app.src
new file mode 100644
index 0000000000..237174358f
--- /dev/null
+++ b/lib/ftp/src/ftp.app.src
@@ -0,0 +1,19 @@
+{application, ftp,
+ [{description, "FTP client"},
+ {vsn, "1.0"},
+ {registered, []},
+ {mod, { ftp_app, []}},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, [
+ ftp,
+ ftp_app,
+ ftp_progress,
+ ftp_response,
+ ftp_sup
+ ]},
+ {runtime_dependencies, ["erts-7.0","stdlib-3.5","kernel-6.0"]}
+ ]}.
diff --git a/lib/ftp/src/ftp.appup.src b/lib/ftp/src/ftp.appup.src
new file mode 100644
index 0000000000..d79c7b60ff
--- /dev/null
+++ b/lib/ftp/src/ftp.appup.src
@@ -0,0 +1,26 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+{"%VSN%",
+ [
+ {<<".*">>,[{restart_application, ftp}]}
+ ],
+ [
+ {<<".*">>,[{restart_application, ftp}]}
+ ]
+}.
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/ftp/src/ftp.erl
index e0430654eb..8790bfec13 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/ftp/src/ftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -18,14 +18,23 @@
%% %CopyrightEnd%
%%
%%
-%% Description: This module implements an ftp client, RFC 959.
-%% It also supports ipv6 RFC 2428 and starttls RFC 4217.
-module(ftp).
-behaviour(gen_server).
--behaviour(inets_service).
+-export([start/0,
+ start_service/1,
+ stop/0,
+ stop_service/1,
+ services/0,
+ service_info/1
+ ]).
+
+%% Added for backward compatibility
+-export([start_standalone/1]).
+
+-export([start_link/1, start_link/2]).
%% API - Client interface
-export([cd/2, close/1, delete/2, formaterror/1,
@@ -47,13 +56,6 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
-%% supervisor callbacks
--export([start_link/1, start_link/2]).
-
-%% Behavior callbacks
--export([start_standalone/1, start_service/1,
- stop_service/1, services/0, service_info/1]).
-
-include("ftp_internal.hrl").
%% Constants used in internal state definition
@@ -67,6 +69,11 @@
-define(FTP_PORT, 21).
-define(FILE_BUFSIZE, 4096).
+
+%%%=========================================================================
+%%% Data Types
+%%%=========================================================================
+
%% Internal state
-record(state, {
csock = undefined, % socket() - Control connection socket
@@ -116,6 +123,61 @@
%%-define(DBG(F,A), io:format(F,A)).
%%-define(DBG(F,A), ct:pal("~p:~p " ++ if is_list(F) -> F; is_atom(F) -> atom_to_list(F) end, [?MODULE,?LINE|A])).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+start() ->
+ application:start(ftp).
+
+start_standalone(Options) ->
+ try
+ {ok, StartOptions} = start_options(Options),
+ {ok, OpenOptions} = open_options(Options),
+ case start_link(StartOptions, []) of
+ {ok, Pid} ->
+ call(Pid, {open, ip_comm, OpenOptions}, plain);
+ Error1 ->
+ Error1
+ end
+ catch
+ throw:Error2 ->
+ Error2
+ end.
+
+start_service(Options) ->
+ try
+ {ok, StartOptions} = start_options(Options),
+ {ok, OpenOptions} = open_options(Options),
+ case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of
+ {ok, Pid} ->
+ call(Pid, {open, ip_comm, OpenOptions}, plain);
+ Error1 ->
+ Error1
+ end
+ catch
+ throw:Error2 ->
+ Error2
+ end.
+
+stop() ->
+ application:stop(ftp).
+
+stop_service(Pid) ->
+ close(Pid).
+
+services() ->
+ [{ftpc, Pid} || {_, Pid, _, _} <-
+ supervisor:which_children(ftp_sup)].
+service_info(Pid) ->
+ {ok, Info} = call(Pid, info, list),
+ {ok, [proplists:lookup(mode, Info),
+ proplists:lookup(local_port, Info),
+ proplists:lookup(peer, Info),
+ proplists:lookup(peer_port, Info)]}.
+
+
%%%=========================================================================
%%% API - CLIENT FUNCTIONS
%%%=========================================================================
@@ -162,22 +224,17 @@ open(Host, Port) when is_integer(Port) ->
%% </BACKWARD-COMPATIBILLITY>
open(Host, Opts) when is_list(Opts) ->
- ?fcrt("open", [{host, Host}, {opts, Opts}]),
try
{ok, StartOptions} = start_options(Opts),
- ?fcrt("open", [{start_options, StartOptions}]),
{ok, OpenOptions} = open_options([{host, Host}|Opts]),
- ?fcrt("open", [{open_options, OpenOptions}]),
case start_link(StartOptions, []) of
{ok, Pid} ->
do_open(Pid, OpenOptions, tls_options(Opts));
Error1 ->
- ?fcrt("open - error", [{error1, Error1}]),
Error1
end
catch
throw:Error2 ->
- ?fcrt("open - error", [{error2, Error2}]),
Error2
end.
@@ -872,219 +929,6 @@ info(Pid) ->
latest_ctrl_response(Pid) ->
call(Pid, latest_ctrl_response, string).
-%%%========================================================================
-%%% Behavior callbacks
-%%%========================================================================
-start_standalone(Options) ->
- try
- {ok, StartOptions} = start_options(Options),
- {ok, OpenOptions} = open_options(Options),
- case start_link(StartOptions, []) of
- {ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
- Error1 ->
- Error1
- end
- catch
- throw:Error2 ->
- Error2
- end.
-
-start_service(Options) ->
- try
- {ok, StartOptions} = start_options(Options),
- {ok, OpenOptions} = open_options(Options),
- case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of
- {ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
- Error1 ->
- Error1
- end
- catch
- throw:Error2 ->
- Error2
- end.
-
-stop_service(Pid) ->
- close(Pid).
-
-services() ->
- [{ftpc, Pid} || {_, Pid, _, _} <-
- supervisor:which_children(ftp_sup)].
-service_info(Pid) ->
- {ok, Info} = call(Pid, info, list),
- {ok, [proplists:lookup(mode, Info),
- proplists:lookup(local_port, Info),
- proplists:lookup(peer, Info),
- proplists:lookup(peer_port, Info)]}.
-
-
-%% This function extracts the start options from the
-%% Valid options:
-%% debug,
-%% verbose
-%% ipfamily
-%% priority
-%% flags (for backward compatibillity)
-start_options(Options) ->
- ?fcrt("start_options", [{options, Options}]),
- case lists:keysearch(flags, 1, Options) of
- {value, {flags, Flags}} ->
- Verbose = lists:member(verbose, Flags),
- IsTrace = lists:member(trace, Flags),
- IsDebug = lists:member(debug, Flags),
- DebugLevel =
- if
- (IsTrace =:= true) ->
- trace;
- IsDebug =:= true ->
- debug;
- true ->
- disable
- end,
- {ok, [{verbose, Verbose},
- {debug, DebugLevel},
- {priority, low}]};
- false ->
- ValidateVerbose =
- fun(true) -> true;
- (false) -> true;
- (_) -> false
- end,
- ValidateDebug =
- fun(trace) -> true;
- (debug) -> true;
- (disable) -> true;
- (_) -> false
- end,
- ValidatePriority =
- fun(low) -> true;
- (normal) -> true;
- (high) -> true;
- (_) -> false
- end,
- ValidOptions =
- [{verbose, ValidateVerbose, false, false},
- {debug, ValidateDebug, false, disable},
- {priority, ValidatePriority, false, low}],
- validate_options(Options, ValidOptions, [])
- end.
-
-
-%% This function extracts and validates the open options from the
-%% Valid options:
-%% mode
-%% host
-%% port
-%% timeout
-%% dtimeout
-%% progress
-%% ftp_extension
-
-open_options(Options) ->
- ?fcrt("open_options", [{options, Options}]),
- ValidateMode =
- fun(active) -> true;
- (passive) -> true;
- (_) -> false
- end,
- ValidateHost =
- fun(Host) when is_list(Host) ->
- true;
- (Host) when is_tuple(Host) andalso
- ((size(Host) =:= 4) orelse (size(Host) =:= 8)) ->
- true;
- (_) ->
- false
- end,
- ValidatePort =
- fun(Port) when is_integer(Port) andalso (Port > 0) -> true;
- (_) -> false
- end,
- ValidateIpFamily =
- fun(inet) -> true;
- (inet6) -> true;
- (inet6fb4) -> true;
- (_) -> false
- end,
- ValidateTimeout =
- fun(Timeout) when is_integer(Timeout) andalso (Timeout >= 0) -> true;
- (_) -> false
- end,
- ValidateDTimeout =
- fun(DTimeout) when is_integer(DTimeout) andalso (DTimeout >= 0) -> true;
- (infinity) -> true;
- (_) -> false
- end,
- ValidateProgress =
- fun(ignore) ->
- true;
- ({Mod, Func, _InitProgress}) when is_atom(Mod) andalso
- is_atom(Func) ->
- true;
- (_) ->
- false
- end,
- ValidateFtpExtension =
- fun(true) -> true;
- (false) -> true;
- (_) -> false
- end,
- ValidOptions =
- [{mode, ValidateMode, false, ?DEFAULT_MODE},
- {host, ValidateHost, true, ehost},
- {port, ValidatePort, false, ?FTP_PORT},
- {ipfamily, ValidateIpFamily, false, inet},
- {timeout, ValidateTimeout, false, ?CONNECTION_TIMEOUT},
- {dtimeout, ValidateDTimeout, false, ?DATA_ACCEPT_TIMEOUT},
- {progress, ValidateProgress, false, ?PROGRESS_DEFAULT},
- {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}],
- validate_options(Options, ValidOptions, []).
-
-tls_options(Options) ->
- %% Options will be validated by ssl application
- proplists:get_value(tls, Options, undefined).
-
-validate_options([], [], Acc) ->
- ?fcrt("validate_options -> done", [{acc, Acc}]),
- {ok, lists:reverse(Acc)};
-validate_options([], ValidOptions, Acc) ->
- ?fcrt("validate_options -> done",
- [{valid_options, ValidOptions}, {acc, Acc}]),
- %% Check if any mandatory options are missing!
- case [{Key, Reason} || {Key, _, true, Reason} <- ValidOptions] of
- [] ->
- Defaults =
- [{Key, Default} || {Key, _, _, Default} <- ValidOptions],
- {ok, lists:reverse(Defaults ++ Acc)};
- [{_, Reason}|_Missing] ->
- throw({error, Reason})
- end;
-validate_options([{Key, Value}|Options], ValidOptions, Acc) ->
- ?fcrt("validate_options -> check",
- [{key, Key}, {value, Value}, {acc, Acc}]),
- case lists:keysearch(Key, 1, ValidOptions) of
- {value, {Key, Validate, _, Default}} ->
- case (catch Validate(Value)) of
- true ->
- ?fcrt("validate_options -> check - accept", []),
- NewValidOptions = lists:keydelete(Key, 1, ValidOptions),
- validate_options(Options, NewValidOptions,
- [{Key, Value} | Acc]);
- _ ->
- ?fcrt("validate_options -> check - reject",
- [{default, Default}]),
- NewValidOptions = lists:keydelete(Key, 1, ValidOptions),
- validate_options(Options, NewValidOptions,
- [{Key, Default} | Acc])
- end;
- false ->
- validate_options(Options, ValidOptions, Acc)
- end;
-validate_options([_|Options], ValidOptions, Acc) ->
- validate_options(Options, ValidOptions, Acc).
-
-
%%%========================================================================
%%% gen_server callback functions
@@ -1183,7 +1027,6 @@ handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid ->
{reply, {error, not_connection_owner}, State};
handle_call({_, {open, ip_comm, Opts}}, From, State) ->
- ?fcrd("handle_call(open)", [{opts, Opts}]),
case key_search(host, Opts, undefined) of
undefined ->
{stop, normal, {error, ehost}, State};
@@ -1203,16 +1046,10 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) ->
dtimeout = DTimeout,
ftp_extension = FtpExt},
- ?fcrd("handle_call(open) -> setup ctrl connection with",
- [{host, Host}, {port, Port}, {timeout, Timeout}]),
case setup_ctrl_connection(Host, Port, Timeout, State2) of
{ok, State3, WaitTimeout} ->
- ?fcrd("handle_call(open) -> ctrl connection setup done",
- [{waittimeout, WaitTimeout}]),
{noreply, State3, WaitTimeout};
- {error, Reason} ->
- ?fcrd("handle_call(open) -> ctrl connection setup failed",
- [{reason, Reason}]),
+ {error, _Reason} ->
gen_server:reply(From, {error, ehost}),
{stop, normal, State2#state{client = undefined}}
end
@@ -1241,7 +1078,7 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
end;
handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) ->
- send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
+ _ = send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
activate_ctrl_connection(State),
{noreply, State#state{client = From, caller = open, tls_options = TLSOptions}};
@@ -1257,7 +1094,7 @@ handle_call({_, {account, Acc}}, From, State)->
handle_user_account(Acc, State#state{client = From});
handle_call({_, pwd}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("PWD", [])),
+ _ = send_ctrl_message(State, mk_cmd("PWD", [])),
activate_ctrl_connection(State),
{noreply, State#state{client = From, caller = pwd}};
@@ -1265,7 +1102,7 @@ handle_call({_, lpwd}, From, #state{ldir = LDir} = State) ->
{reply, {ok, LDir}, State#state{client = From}};
handle_call({_, {cd, Dir}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
+ _ = send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
activate_ctrl_connection(State),
{noreply, State#state{client = From, caller = cd}};
@@ -1284,35 +1121,35 @@ handle_call({_, {dir, Len, Dir}}, {_Pid, _} = From,
client = From});
handle_call({_, {rename, CurrFile, NewFile}}, From,
#state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("RNFR ~s", [CurrFile])),
+ _ = send_ctrl_message(State, mk_cmd("RNFR ~s", [CurrFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {rename, NewFile}, client = From}};
handle_call({_, {delete, File}}, {_Pid, _} = From,
#state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("DELE ~s", [File])),
+ _ = send_ctrl_message(State, mk_cmd("DELE ~s", [File])),
activate_ctrl_connection(State),
{noreply, State#state{client = From}};
handle_call({_, {mkdir, Dir}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("MKD ~s", [Dir])),
+ _ = send_ctrl_message(State, mk_cmd("MKD ~s", [Dir])),
activate_ctrl_connection(State),
{noreply, State#state{client = From}};
handle_call({_,{rmdir, Dir}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd("RMD ~s", [Dir])),
+ _ = send_ctrl_message(State, mk_cmd("RMD ~s", [Dir])),
activate_ctrl_connection(State),
{noreply, State#state{client = From}};
handle_call({_,{type, Type}}, From, #state{chunk = false} = State) ->
case Type of
ascii ->
- send_ctrl_message(State, mk_cmd("TYPE A", [])),
+ _ = send_ctrl_message(State, mk_cmd("TYPE A", [])),
activate_ctrl_connection(State),
{noreply, State#state{caller = type, type = ascii,
client = From}};
binary ->
- send_ctrl_message(State, mk_cmd("TYPE I", [])),
+ _ = send_ctrl_message(State, mk_cmd("TYPE I", [])),
activate_ctrl_connection(State),
{noreply, State#state{caller = type, type = binary,
client = From}};
@@ -1423,7 +1260,7 @@ handle_call({_, chunk_end}, _, #state{chunk = false} = State) ->
{reply, {error, echunk}, State};
handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) ->
- send_ctrl_message(State, mk_cmd(Cmd, [])),
+ _ = send_ctrl_message(State, mk_cmd(Cmd, [])),
activate_ctrl_connection(State),
{noreply, State#state{client = From, caller = quote}};
@@ -1447,7 +1284,7 @@ handle_call(Request, _Timeout, State) ->
%% Description: Handles cast messages.
%%-------------------------------------------------------------------------
handle_cast({Pid, close}, #state{owner = Pid} = State) ->
- send_ctrl_message(State, mk_cmd("QUIT", [])),
+ _ = send_ctrl_message(State, mk_cmd("QUIT", [])),
close_ctrl_connection(State),
close_data_connection(State),
{stop, normal, State#state{csock = undefined, dsock = undefined}};
@@ -1743,17 +1580,17 @@ start_link(Opts, GenServerOptions) ->
%%--------------------------------------------------------------------------
%% User handling
handle_user(User, Password, Acc, State) ->
- send_ctrl_message(State, mk_cmd("USER ~s", [User])),
+ _ = send_ctrl_message(State, mk_cmd("USER ~s", [User])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {handle_user, Password, Acc}}}.
handle_user_passwd(Password, Acc, State) ->
- send_ctrl_message(State, mk_cmd("PASS ~s", [Password])),
+ _ = send_ctrl_message(State, mk_cmd("PASS ~s", [Password])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {handle_user_passwd, Acc}}}.
handle_user_account(Acc, State) ->
- send_ctrl_message(State, mk_cmd("ACCT ~s", [Acc])),
+ _ = send_ctrl_message(State, mk_cmd("ACCT ~s", [Acc])),
activate_ctrl_connection(State),
{noreply, State#state{caller = handle_user_account}}.
@@ -1770,7 +1607,7 @@ handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
case ssl:connect(Socket, TLSOptions, Timeout) of
{ok, TLSSocket} ->
State = State0#state{csock = {ssl,TLSSocket}},
- send_ctrl_message(State, mk_cmd("PBSZ 0", [])),
+ _ = send_ctrl_message(State, mk_cmd("PBSZ 0", [])),
activate_ctrl_connection(State),
{noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} };
{error, _} = Error ->
@@ -1781,7 +1618,7 @@ handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
end;
handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) ->
- send_ctrl_message(State, mk_cmd("PROT P", [])),
+ _ = send_ctrl_message(State, mk_cmd("PROT P", [])),
activate_ctrl_connection(State),
{noreply, State#state{tls_upgrading_data_connection = {true, prot}}};
@@ -1975,7 +1812,7 @@ handle_ctrl_result({pos_compl, Lines},
#state{caller = {handle_dir_data, Dir, DirData}} =
State) ->
OldDir = pwd_result(Lines),
- send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
+ _ = send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {handle_dir_data_second_phase, OldDir,
DirData}}};
@@ -1991,7 +1828,7 @@ handle_ctrl_result(S={_Status, _},
handle_ctrl_result({pos_compl, _},
#state{caller = {handle_dir_data_second_phase, OldDir,
DirData}} = State) ->
- send_ctrl_message(State, mk_cmd("CWD ~s", [OldDir])),
+ _ = send_ctrl_message(State, mk_cmd("CWD ~s", [OldDir])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {handle_dir_data_third_phase, DirData}}};
handle_ctrl_result({Status, _},
@@ -2013,7 +1850,7 @@ handle_ctrl_result(Status={epath, _}, #state{caller = {dir,_}} = State) ->
%% File renaming
handle_ctrl_result({pos_interm, _}, #state{caller = {rename, NewFile}}
= State) ->
- send_ctrl_message(State, mk_cmd("RNTO ~s", [NewFile])),
+ _ = send_ctrl_message(State, mk_cmd("RNTO ~s", [NewFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = rename_second_phase}};
@@ -2190,28 +2027,28 @@ handle_caller(#state{caller = {dir, Dir, Len}} = State) ->
short -> "NLST";
long -> "LIST"
end,
- case Dir of
- "" ->
- send_ctrl_message(State, mk_cmd(Cmd, ""));
- _ ->
- send_ctrl_message(State, mk_cmd(Cmd ++ " ~s", [Dir]))
- end,
+ _ = case Dir of
+ "" ->
+ send_ctrl_message(State, mk_cmd(Cmd, ""));
+ _ ->
+ send_ctrl_message(State, mk_cmd(Cmd ++ " ~s", [Dir]))
+ end,
activate_ctrl_connection(State),
{noreply, State#state{caller = {dir, Dir}}};
handle_caller(#state{caller = {recv_bin, RemoteFile}} = State) ->
- send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
+ _ = send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = recv_bin}};
handle_caller(#state{caller = {start_chunk_transfer, Cmd, RemoteFile}} =
State) ->
- send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
+ _ = send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = start_chunk_transfer}};
handle_caller(#state{caller = {recv_file, RemoteFile, Fd}} = State) ->
- send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
+ _ = send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {recv_file, Fd}}};
@@ -2219,7 +2056,7 @@ handle_caller(#state{caller = {transfer_file, {Cmd, LocalFile, RemoteFile}},
ldir = LocalDir, client = From} = State) ->
case file_open(filename:absname(LocalFile, LocalDir), read) of
{ok, Fd} ->
- send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
+ _ = send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {transfer_file, Fd}}};
{error, _} ->
@@ -2230,7 +2067,7 @@ handle_caller(#state{caller = {transfer_file, {Cmd, LocalFile, RemoteFile}},
handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} =
State) ->
- send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
+ _ = send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {transfer_data, Bin}}}.
@@ -2244,7 +2081,7 @@ setup_ctrl_connection(Host, Port, Timeout, State) ->
{ok, IpFam, CSock} ->
NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam},
activate_ctrl_connection(NewState),
- case Timeout - inets_lib:millisec_passed(MsTime) of
+ case Timeout - millisec_passed(MsTime) of
Timeout2 when (Timeout2 >= 0) ->
{ok, NewState#state{caller = open}, Timeout2};
_ ->
@@ -2267,7 +2104,7 @@ setup_data_connection(#state{mode = active,
{ok, {_, Port}} = sockname({tcp,LSock}),
IpAddress = inet_parse:ntoa(IP),
Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]),
- send_ctrl_message(State, Cmd),
+ _ = send_ctrl_message(State, Cmd),
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}};
@@ -2275,18 +2112,18 @@ setup_data_connection(#state{mode = active,
{ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false},
binary, {packet, 0}]),
{ok, Port} = inet:port(LSock),
- case FtpExt of
- false ->
- {IP1, IP2, IP3, IP4} = IP,
- {Port1, Port2} = {Port div 256, Port rem 256},
- send_ctrl_message(State,
- mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
- [IP1, IP2, IP3, IP4, Port1, Port2]));
- true ->
- IpAddress = inet_parse:ntoa(IP),
- Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
- send_ctrl_message(State, Cmd)
- end,
+ _ = case FtpExt of
+ false ->
+ {IP1, IP2, IP3, IP4} = IP,
+ {Port1, Port2} = {Port div 256, Port rem 256},
+ send_ctrl_message(State,
+ mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
+ [IP1, IP2, IP3, IP4, Port1, Port2]));
+ true ->
+ IpAddress = inet_parse:ntoa(IP),
+ Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
+ send_ctrl_message(State, Cmd)
+ end,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}}
@@ -2294,21 +2131,21 @@ setup_data_connection(#state{mode = active,
setup_data_connection(#state{mode = passive, ipfamily = inet6,
caller = Caller} = State) ->
- send_ctrl_message(State, mk_cmd("EPSV", [])),
+ _ = send_ctrl_message(State, mk_cmd("EPSV", [])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection, Caller}}};
setup_data_connection(#state{mode = passive, ipfamily = inet,
caller = Caller,
ftp_extension = false} = State) ->
- send_ctrl_message(State, mk_cmd("PASV", [])),
+ _ = send_ctrl_message(State, mk_cmd("PASV", [])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection, Caller}}};
setup_data_connection(#state{mode = passive, ipfamily = inet,
caller = Caller,
ftp_extension = true} = State) ->
- send_ctrl_message(State, mk_cmd("EPSV", [])),
+ _ = send_ctrl_message(State, mk_cmd("EPSV", [])),
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection, Caller}}}.
@@ -2594,3 +2431,166 @@ start_chunk(#state{client = From} = State) ->
State#state{chunk = true,
client = undefined,
caller = undefined}.
+
+
+%% This function extracts the start options from the
+%% Valid options:
+%% debug,
+%% verbose
+%% ipfamily
+%% priority
+%% flags (for backward compatibillity)
+start_options(Options) ->
+ case lists:keysearch(flags, 1, Options) of
+ {value, {flags, Flags}} ->
+ Verbose = lists:member(verbose, Flags),
+ IsTrace = lists:member(trace, Flags),
+ IsDebug = lists:member(debug, Flags),
+ DebugLevel =
+ if
+ (IsTrace =:= true) ->
+ trace;
+ IsDebug =:= true ->
+ debug;
+ true ->
+ disable
+ end,
+ {ok, [{verbose, Verbose},
+ {debug, DebugLevel},
+ {priority, low}]};
+ false ->
+ ValidateVerbose =
+ fun(true) -> true;
+ (false) -> true;
+ (_) -> false
+ end,
+ ValidateDebug =
+ fun(trace) -> true;
+ (debug) -> true;
+ (disable) -> true;
+ (_) -> false
+ end,
+ ValidatePriority =
+ fun(low) -> true;
+ (normal) -> true;
+ (high) -> true;
+ (_) -> false
+ end,
+ ValidOptions =
+ [{verbose, ValidateVerbose, false, false},
+ {debug, ValidateDebug, false, disable},
+ {priority, ValidatePriority, false, low}],
+ validate_options(Options, ValidOptions, [])
+ end.
+
+
+%% This function extracts and validates the open options from the
+%% Valid options:
+%% mode
+%% host
+%% port
+%% timeout
+%% dtimeout
+%% progress
+%% ftp_extension
+
+open_options(Options) ->
+ ValidateMode =
+ fun(active) -> true;
+ (passive) -> true;
+ (_) -> false
+ end,
+ ValidateHost =
+ fun(Host) when is_list(Host) ->
+ true;
+ (Host) when is_tuple(Host) andalso
+ ((size(Host) =:= 4) orelse (size(Host) =:= 8)) ->
+ true;
+ (_) ->
+ false
+ end,
+ ValidatePort =
+ fun(Port) when is_integer(Port) andalso (Port > 0) -> true;
+ (_) -> false
+ end,
+ ValidateIpFamily =
+ fun(inet) -> true;
+ (inet6) -> true;
+ (inet6fb4) -> true;
+ (_) -> false
+ end,
+ ValidateTimeout =
+ fun(Timeout) when is_integer(Timeout) andalso (Timeout >= 0) -> true;
+ (_) -> false
+ end,
+ ValidateDTimeout =
+ fun(DTimeout) when is_integer(DTimeout) andalso (DTimeout >= 0) -> true;
+ (infinity) -> true;
+ (_) -> false
+ end,
+ ValidateProgress =
+ fun(ignore) ->
+ true;
+ ({Mod, Func, _InitProgress}) when is_atom(Mod) andalso
+ is_atom(Func) ->
+ true;
+ (_) ->
+ false
+ end,
+ ValidateFtpExtension =
+ fun(true) -> true;
+ (false) -> true;
+ (_) -> false
+ end,
+ ValidOptions =
+ [{mode, ValidateMode, false, ?DEFAULT_MODE},
+ {host, ValidateHost, true, ehost},
+ {port, ValidatePort, false, ?FTP_PORT},
+ {ipfamily, ValidateIpFamily, false, inet},
+ {timeout, ValidateTimeout, false, ?CONNECTION_TIMEOUT},
+ {dtimeout, ValidateDTimeout, false, ?DATA_ACCEPT_TIMEOUT},
+ {progress, ValidateProgress, false, ?PROGRESS_DEFAULT},
+ {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}],
+ validate_options(Options, ValidOptions, []).
+
+tls_options(Options) ->
+ %% Options will be validated by ssl application
+ proplists:get_value(tls, Options, undefined).
+
+validate_options([], [], Acc) ->
+ {ok, lists:reverse(Acc)};
+validate_options([], ValidOptions, Acc) ->
+ %% Check if any mandatory options are missing!
+ case [{Key, Reason} || {Key, _, true, Reason} <- ValidOptions] of
+ [] ->
+ Defaults =
+ [{Key, Default} || {Key, _, _, Default} <- ValidOptions],
+ {ok, lists:reverse(Defaults ++ Acc)};
+ [{_, Reason}|_Missing] ->
+ throw({error, Reason})
+ end;
+validate_options([{Key, Value}|Options], ValidOptions, Acc) ->
+ case lists:keysearch(Key, 1, ValidOptions) of
+ {value, {Key, Validate, _, Default}} ->
+ case (catch Validate(Value)) of
+ true ->
+ NewValidOptions = lists:keydelete(Key, 1, ValidOptions),
+ validate_options(Options, NewValidOptions,
+ [{Key, Value} | Acc]);
+ _ ->
+ NewValidOptions = lists:keydelete(Key, 1, ValidOptions),
+ validate_options(Options, NewValidOptions,
+ [{Key, Default} | Acc])
+ end;
+ false ->
+ validate_options(Options, ValidOptions, Acc)
+ end;
+validate_options([_|Options], ValidOptions, Acc) ->
+ validate_options(Options, ValidOptions, Acc).
+
+%% Help function, elapsed milliseconds since T0
+millisec_passed(T0) ->
+ %% OTP 18
+ erlang:convert_time_unit(erlang:monotonic_time() - T0,
+ native,
+ micro_seconds) div 1000.
diff --git a/lib/ftp/src/ftp_app.erl b/lib/ftp/src/ftp_app.erl
new file mode 100644
index 0000000000..d647d9fce3
--- /dev/null
+++ b/lib/ftp/src/ftp_app.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%%-------------------------------------------------------------------
+%% @doc ftp public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(ftp_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ ftp_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/lib/inets/src/ftp/ftp_internal.hrl b/lib/ftp/src/ftp_internal.hrl
index f29bb4a099..84f980e8fd 100644
--- a/lib/inets/src/ftp/ftp_internal.hrl
+++ b/lib/ftp/src/ftp_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,12 +22,14 @@
-ifndef(ftp_internal_hrl).
-define(ftp_internal_hrl, true).
--include_lib("inets/src/inets_app/inets_internal.hrl").
-
--define(SERVICE, ftpc).
--define(fcri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
--define(fcrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
--define(fcrd(Label, Content), ?report_debug(Label, ?SERVICE, Content)).
--define(fcrt(Label, Content), ?report_trace(Label, ?SERVICE, Content)).
+-define(CR, $\r).
+-define(LF, $\n).
+-define(CRLF, [$\r,$\n]).
+-define(SP, $\s).
+-define(TAB, $\t).
+-define(LEFT_PAREN, $().
+-define(RIGHT_PAREN, $)).
+-define(WHITE_SPACE, $ ).
+-define(DOUBLE_QUOTE, $").
-endif. % -ifdef(ftp_internal_hrl).
diff --git a/lib/inets/src/ftp/ftp_progress.erl b/lib/ftp/src/ftp_progress.erl
index a6263e5cd7..a6263e5cd7 100644
--- a/lib/inets/src/ftp/ftp_progress.erl
+++ b/lib/ftp/src/ftp_progress.erl
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/ftp/src/ftp_response.erl
index d54d97dc91..d54d97dc91 100644
--- a/lib/inets/src/ftp/ftp_response.erl
+++ b/lib/ftp/src/ftp_response.erl
diff --git a/lib/ftp/src/ftp_sup.erl b/lib/ftp/src/ftp_sup.erl
new file mode 100644
index 0000000000..f30046802f
--- /dev/null
+++ b/lib/ftp/src/ftp_sup.erl
@@ -0,0 +1,68 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%%-------------------------------------------------------------------
+%% @doc ftp top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(ftp_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_child/1, start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+start_child(Args) ->
+ supervisor:start_child(?MODULE, Args).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+init(_) ->
+ SupFlags = #{strategy => simple_one_for_one,
+ intensity => 0,
+ period => 3600},
+ {ok, {SupFlags, child_specs()}}.
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+child_specs() ->
+ [#{id => undefined,
+ start => {ftp, start_link, []},
+ restart => temporary,
+ shutdown => 4000,
+ type => worker,
+ modules => [ftp]}].
diff --git a/lib/ftp/test/Makefile b/lib/ftp/test/Makefile
new file mode 100644
index 0000000000..147f8e5dd6
--- /dev/null
+++ b/lib/ftp/test/Makefile
@@ -0,0 +1,251 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2018. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+#
+# For an outline of how this all_SUITE_data stuff works, see the
+# make file ../../ssl/test/Makefile.
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN = $(FTP_VSN)
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+INCLUDES = -I. \
+ -I$(ERL_TOP)/lib/ftp/src
+
+CP = cp
+
+ifeq ($(TESTROOT_DIR),)
+TESTROOT_DIR = /ldisk/tests/$(USER)/ftp
+endif
+
+ifeq ($(FTP_DATA_DIR),)
+FTP_DATA_DIR = $(TESTROOT_DIR)/data_dir
+endif
+
+ifeq ($(FTP_PRIV_DIR),)
+FTP_PRIV_DIR = $(TESTROOT_DIR)/priv_dir
+endif
+
+FTP_FLAGS = -Dftp__data_dir='"$(FTP_DATA_DIR)"' \
+ -Dftp_priv_dir='"$(FTP_PRIV_DIR)"'
+
+
+###
+### test suite debug flags
+###
+ifeq ($(FTP_DEBUG_CLIENT),)
+ FTP_DEBUG_CLIENT = y
+endif
+
+ifeq ($(FTP_DEBUG_CLIENT),)
+ FTP_FLAGS += -Dftp_debug_client
+endif
+
+ifeq ($(FTP_TRACE_CLIENT),)
+ FTP_DEBUG_CLIENT = y
+endif
+
+ifeq ($(FTP_TRACE_CLIENT),y)
+ FTP_FLAGS += -Dftp_trace_client
+endif
+
+ifneq ($(FTP_DEBUG),)
+ FTP_DEBUG = s
+endif
+
+ifeq ($(FTP_DEBUG),l)
+ FTP_FLAGS += -Dftp_log
+endif
+
+ifeq ($(FTP_DEBUG),d)
+ FTP_FLAGS += -Dftp_debug -Dftp_log
+endif
+
+
+FTP_FLAGS += -pa ../ftp/ebin
+
+FTP_ROOT = ../ftp
+
+MODULES = \
+ erl_make_certs \
+ ftp_SUITE \
+ ftp_format_SUITE \
+ ftp_test_lib
+
+
+EBIN = .
+
+HRL_FILES = \
+ ftp_internal.hrl
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+SOURCE = $(ERL_FILES) $(HRL_FILES)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+FTP_SPECS = ftp.spec ftp_bench.spec
+COVER_FILE = ftp.cover
+FTP_FILES = ftp.config $(FTP_SPECS)
+
+
+FTP_DATADIRS = ftp_SUITE_data
+
+DATADIRS = $(FTP_DATADIRS)
+
+EMAKEFILE = Emakefile
+MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
+
+ifeq ($(MAKE_EMAKE),)
+BUILDTARGET = $(TARGET_FILES)
+RELTEST_FILES = $(COVER_FILE) $(FTP_SPECS) $(SOURCE)
+else
+BUILDTARGET = emakebuild
+RELTEST_FILES = $(EMAKEFILE) $(COVER_FILE) $(FTP_SPECS) $(SOURCE)
+endif
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+
+RELTESTSYSDIR = "$(RELEASE_PATH)/ftp_test"
+RELTESTSYSALLDATADIR = $(RELTESTSYSDIR)/all_SUITE_data
+RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
+
+
+# ----------------------------------------------------
+# FLAGS
+# The path to the test_server ebin dir is needed when
+# running the target "targets".
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += \
+ $(INCLUDES) \
+ $(FTP_FLAGS)
+
+# ----------------------------------------------------
+# Targets
+# erl -sname kalle -pa ../ebin
+# If you intend to run the test suite locally (private), then
+# there is some requirements:
+# 1) FTP_PRIV_DIR must be created
+# ----------------------------------------------------
+
+tests debug opt: $(BUILDTARGET)
+
+targets: $(TARGET_FILES)
+
+.PHONY: emakebuild
+
+emakebuild: $(EMAKEFILE)
+
+$(EMAKEFILE):
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE)
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE)
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) "$(RELSYSDIR)/test"
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/test"
+ $(INSTALL_DATA) $(FTP_FILES) "$(RELSYSDIR)/test"
+ @for d in $(DATADIRS); do \
+ echo "installing data dir $$d"; \
+ if test -f $$d/TAR.exclude; then \
+ echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
+ cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
+ find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
+ find $$d -name '*~' >> $$d/TAR.exclude2; \
+ find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
+ find $$d -name 'core' >> $$d/TAR.exclude2; \
+ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
+ tar cfX - $$d/TAR.exclude2 $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \
+ else \
+ tar cf - $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \
+ fi; \
+ done
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELTESTSYSDIR)
+ $(INSTALL_DATA) $(RELTEST_FILES) $(RELTESTSYSDIR)
+ chmod -R u+w $(RELTESTSYSDIR)
+ tar chf - $(DATADIRS) | (cd $(RELTESTSYSDIR); tar xf -)
+ $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)
+ $(INSTALL_DIR) $(RELTESTSYSBINDIR)
+ chmod -R +x $(RELTESTSYSBINDIR)
+ $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)/win32/lib
+
+release_docs_spec:
+
+info:
+ @echo "MAKE_EMAKE = $(MAKE_EMAKE)"
+ @echo "EMAKEFILE = $(EMAKEFILE)"
+ @echo "BUILDTARGET = $(BUILDTARGET)"
+ @echo ""
+ @echo "MODULES = $(MODULES)"
+ @echo "ERL_FILES = $(ERL_FILES)"
+ @echo "SOURCE = $(SOURCE)"
+ @echo "TARGET_FILES = $(TARGET_FILES)"
+ @echo ""
+ @echo "FTP_SPECS = $(FTP_SPECS)"
+ @echo "FTP_FILES = $(FTP_FILES)"
+ @echo ""
+ @echo "RELEASE_PATH = "$(RELEASE_PATH)""
+ @echo "RELSYSDIR = "$(RELSYSDIR)""
+ @echo "RELTESTSYSDIR = $(RELTESTSYSDIR)"
+ @echo "RELTESTSYSALLDATADIR = $(RELTESTSYSALLDATADIR)"
+ @echo "RELTESTSYSBINDIR = $(RELTESTSYSBINDIR)"
+ @echo ""
+ @echo "DATADIRS = $(DATADIRS)"
+ @echo "REL_DATADIRS = $(REL_DATADIRS)"
+ @echo ""
+ @echo "FTP_DATA_DIR = $(FTP_DATA_DIR)"
+ @echo "FTP_PRIV_DIR = $(FTP_PRIV_DIR)"
+ @echo "FTP_ROOT = $(FTP_ROOT)"
+ @echo "FTP_FLAGS = $(FTP_FLAGS)"
+
+
diff --git a/lib/ftp/test/erl_make_certs.erl b/lib/ftp/test/erl_make_certs.erl
new file mode 100644
index 0000000000..2db95825bc
--- /dev/null
+++ b/lib/ftp/test/erl_make_certs.erl
@@ -0,0 +1,475 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Create test certificates
+
+-module(erl_make_certs).
+-include_lib("public_key/include/public_key.hrl").
+
+-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]).
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% @doc Create and return a der encoded certificate
+%% Option Default
+%% -------------------------------------------------------
+%% digest sha1
+%% validity {date(), date() + week()}
+%% version 3
+%% subject [] list of the following content
+%% {name, Name}
+%% {email, Email}
+%% {city, City}
+%% {state, State}
+%% {org, Org}
+%% {org_unit, OrgUnit}
+%% {country, Country}
+%% {serial, Serial}
+%% {title, Title}
+%% {dnQualifer, DnQ}
+%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created)
+%% (obs IssuerKey migth be {Key, Password}
+%% key = KeyFile|KeyBin|rsa|dsa|ec Subject PublicKey rsa, dsa or ec generates key
+%%
+%%
+%% (OBS: The generated keys are for testing only)
+%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
+%% @end
+%%--------------------------------------------------------------------
+
+make_cert(Opts) ->
+ SubjectPrivateKey = get_key(Opts),
+ {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
+ Cert = public_key:pkix_sign(TBSCert, IssuerKey),
+ true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
+ {Cert, encode_key(SubjectPrivateKey)}.
+
+%%--------------------------------------------------------------------
+%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
+%% @spec (::string(), ::string(), {Cert,Key}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
+ ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
+ [{'Certificate', Cert, not_encrypted}]),
+ ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
+
+%%--------------------------------------------------------------------
+%% @doc Creates a rsa key (OBS: for testing only)
+%% the size are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_rsa(Size) when is_integer(Size) ->
+ Key = gen_rsa2(Size),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Creates a ec key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_ec(Curve) when is_atom(Curve) ->
+ Key = gen_ec2(Curve),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Verifies cert signatures
+%% @spec (::binary(), ::tuple()) -> ::boolean()
+%% @end
+%%--------------------------------------------------------------------
+verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
+ Key = decode_key(DerKey),
+ case Key of
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
+ public_key:pkix_verify(DerEncodedCert,
+ #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
+ public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}});
+ #'ECPrivateKey'{version = _Version, privateKey = _PrivKey,
+ parameters = Params, publicKey = {0, PubKey}} ->
+ public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_key(Opts) ->
+ case proplists:get_value(key, Opts) of
+ undefined -> make_key(rsa, Opts);
+ rsa -> make_key(rsa, Opts);
+ dsa -> make_key(dsa, Opts);
+ ec -> make_key(ec, Opts);
+ Key ->
+ Password = proplists:get_value(password, Opts, no_passwd),
+ decode_key(Key, Password)
+ end.
+
+decode_key({Key, Pw}) ->
+ decode_key(Key, Pw);
+decode_key(Key) ->
+ decode_key(Key, no_passwd).
+
+
+decode_key(#'RSAPublicKey'{} = Key,_) ->
+ Key;
+decode_key(#'RSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'DSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'ECPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(PemEntry = {_,_,_}, Pw) ->
+ public_key:pem_entry_decode(PemEntry, Pw);
+decode_key(PemBin, Pw) ->
+ [KeyInfo] = public_key:pem_decode(PemBin),
+ decode_key(KeyInfo, Pw).
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {'RSAPrivateKey', Der, not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', Der, not_encrypted};
+encode_key(Key = #'ECPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('ECPrivateKey', Key),
+ {'ECPrivateKey', Der, not_encrypted}.
+
+make_tbs(SubjectKey, Opts) ->
+ Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
+
+ IssuerProp = proplists:get_value(issuer, Opts, true),
+ {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey),
+
+ {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
+
+ SignAlgo = #'SignatureAlgorithm'{algorithm = Algo,
+ parameters = Parameters},
+ Subject = case IssuerProp of
+ true -> %% Is a Root Ca
+ Issuer;
+ _ ->
+ subject(proplists:get_value(subject, Opts),false)
+ end,
+
+ {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
+ signature = SignAlgo,
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Subject,
+ subjectPublicKeyInfo = publickey(SubjectKey),
+ version = Version,
+ extensions = extensions(Opts)
+ }, IssuerKey}.
+
+issuer(true, Opts, SubjectKey) ->
+ %% Self signed
+ {subject(proplists:get_value(subject, Opts), true), SubjectKey};
+issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
+ {issuer_der(Issuer), decode_key(IssuerKey)};
+issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
+ {ok, [{cert, Cert, _}|_]} = pem_to_der(File),
+ {issuer_der(Cert), decode_key(IssuerKey)}.
+
+issuer_der(Issuer) ->
+ Decoded = public_key:pkix_decode_cert(Issuer, otp),
+ #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
+ #'OTPTBSCertificate'{subject=Subject} = Tbs,
+ Subject.
+
+subject(undefined, IsRootCA) ->
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER", "test_user") end,
+ Opts = [{email, User ++ "@erlang.org"},
+ {name, User},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+ subject(Opts);
+subject(Opts, _) ->
+ subject(Opts).
+
+subject(SubjectOpts) when is_list(SubjectOpts) ->
+ Encode = fun(Opt) ->
+ {Type,Value} = subject_enc(Opt),
+ [#'AttributeTypeAndValue'{type=Type, value=Value}]
+ end,
+ {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
+
+%% Fill in the blanks
+subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
+subject_enc({email, Email}) -> {?'id-emailAddress', Email};
+subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
+subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}};
+subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
+subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
+subject_enc({country, Country}) -> {?'id-at-countryName', Country};
+subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial};
+subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}};
+subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ};
+subject_enc(Other) -> Other.
+
+
+extensions(Opts) ->
+ case proplists:get_value(extensions, Opts, []) of
+ false ->
+ asn1_NOVALUE;
+ Exts ->
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
+ end.
+
+default_extensions(Exts) ->
+ Def = [{key_usage,undefined},
+ {subject_altname, undefined},
+ {issuer_altname, undefined},
+ {basic_constraints, default},
+ {name_constraints, undefined},
+ {policy_constraints, undefined},
+ {ext_key_usage, undefined},
+ {inhibit_any, undefined},
+ {auth_key_id, undefined},
+ {subject_key_id, undefined},
+ {policy_mapping, undefined}],
+ Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
+ Exts ++ lists:foldl(Filter, Def, Exts).
+
+extension({_, undefined}) -> [];
+extension({basic_constraints, Data}) ->
+ case Data of
+ default ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true},
+ critical=true};
+ false ->
+ [];
+ Len when is_integer(Len) ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
+ critical=true};
+ _ ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = Data}
+ end;
+extension({Id, Data, Critical}) ->
+ #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
+
+
+publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
+ Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = Public};
+publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
+ parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y};
+publickey(#'ECPrivateKey'{version = _Version,
+ privateKey = _PrivKey,
+ parameters = Params,
+ publicKey = {0, PubKey}}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = #'ECPoint'{point = PubKey}}.
+
+validity(Opts) ->
+ DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
+ DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
+ {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
+ Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
+ #'Validity'{notBefore={generalTime, Format(DefFrom)},
+ notAfter ={generalTime, Format(DefTo)}}.
+
+sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'sha1WithRSAEncryption';
+ sha512 -> ?'sha512WithRSAEncryption';
+ sha384 -> ?'sha384WithRSAEncryption';
+ sha256 -> ?'sha256WithRSAEncryption';
+ md5 -> ?'md5WithRSAEncryption';
+ md2 -> ?'md2WithRSAEncryption'
+ end,
+ {Type, 'NULL'};
+sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
+ {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}};
+sign_algorithm(#'ECPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'ecdsa-with-SHA1';
+ sha512 -> ?'ecdsa-with-SHA512';
+ sha384 -> ?'ecdsa-with-SHA384';
+ sha256 -> ?'ecdsa-with-SHA256'
+ end,
+ {Type, 'NULL'}.
+
+make_key(rsa, _Opts) ->
+ %% (OBS: for testing only)
+ gen_rsa2(64);
+make_key(dsa, _Opts) ->
+ gen_dsa2(128, 20); %% Bytes i.e. {1024, 160}
+make_key(ec, _Opts) ->
+ %% (OBS: for testing only)
+ gen_ec2(secp256k1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% RSA key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
+ 47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
+
+gen_rsa2(Size) ->
+ P = prime(Size),
+ Q = prime(Size),
+ N = P*Q,
+ Tot = (P - 1) * (Q - 1),
+ [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
+ {D1,D2} = extended_gcd(E, Tot),
+ D = erlang:max(D1,D2),
+ case D < E of
+ true ->
+ gen_rsa2(Size);
+ false ->
+ {Co1,Co2} = extended_gcd(Q, P),
+ Co = erlang:max(Co1,Co2),
+ #'RSAPrivateKey'{version = 'two-prime',
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ exponent1 = D rem (P-1),
+ exponent2 = D rem (Q-1),
+ coefficient = Co
+ }
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p = P, q = Q,
+ g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EC key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+gen_ec2(CurveId) ->
+ {PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId),
+
+ #'ECPrivateKey'{version = 1,
+ privateKey = binary_to_list(PrivKey),
+ parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)},
+ publicKey = {0, PubKey}}.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(P, 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ prime_odd(Rand, 0).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ prime_odd(Rand+2, N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(10000, Candidate),
+ Result = crypto:mod_pow(CoPrime, Candidate, Candidate) ,
+ is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test).
+
+is_prime(CoPrime, CoPrime, Candidate, Test) ->
+ is_prime(Candidate, Test-1);
+is_prime(_,_,_,_) ->
+ false.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(Min, Max).
+
+odd_rand(Min,Max) ->
+ Rand = crypto:rand_uniform(Min,Max),
+ case Rand rem 2 of
+ 0 ->
+ Rand + 1;
+ _ ->
+ Rand
+ end.
+
+extended_gcd(A, B) ->
+ case A rem B of
+ 0 ->
+ {0, 1};
+ N ->
+ {X, Y} = extended_gcd(B, N),
+ {Y, X-Y*(A div B)}
+ end.
+
+pem_to_der(File) ->
+ {ok, PemBin} = file:read_file(File),
+ public_key:pem_decode(PemBin).
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
+
diff --git a/lib/ftp/test/ftp.config b/lib/ftp/test/ftp.config
new file mode 100644
index 0000000000..2600237da9
--- /dev/null
+++ b/lib/ftp/test/ftp.config
@@ -0,0 +1 @@
+[]. \ No newline at end of file
diff --git a/lib/ftp/test/ftp.cover b/lib/ftp/test/ftp.cover
new file mode 100644
index 0000000000..5b155991bc
--- /dev/null
+++ b/lib/ftp/test/ftp.cover
@@ -0,0 +1,2 @@
+{incl_app,ftp,details}.
+
diff --git a/lib/ftp/test/ftp.spec b/lib/ftp/test/ftp.spec
new file mode 100644
index 0000000000..faf1e532a8
--- /dev/null
+++ b/lib/ftp/test/ftp.spec
@@ -0,0 +1 @@
+{suites,"../ftp_test", all}.
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/ftp/test/ftp_SUITE.erl
index 3dfec01ba2..92d2c36a86 100644
--- a/lib/inets/test/ftp_SUITE.erl
+++ b/lib/ftp/test/ftp_SUITE.erl
@@ -18,16 +18,10 @@
%% %CopyrightEnd%
%%
%%
-
-%%
-%% ct:run("../inets_test", ftp_SUITE).
-%%
-
-module(ftp_SUITE).
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
--include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
@@ -59,6 +53,9 @@ all() ->
{group, ftp_active},
{group, ftps_passive},
{group, ftps_active},
+ {group, ftp_sup},
+ app,
+ appup,
error_ehost,
clean_shutdown
].
@@ -68,7 +65,8 @@ groups() ->
{ftp_passive, [], ftp_tests()},
{ftp_active, [], ftp_tests()},
{ftps_passive, [], ftp_tests()},
- {ftps_active, [], ftp_tests()}
+ {ftps_active, [], ftp_tests()},
+ {ftp_sup, [], ftp_sup_tests()}
].
ftp_tests()->
@@ -109,6 +107,12 @@ ftp_tests()->
unexpected_bang
].
+ftp_sup_tests() ->
+ [
+ start_ftp,
+ ftp_worker
+ ].
+
%%--------------------------------------------------------------------
%%% Config
@@ -181,7 +185,8 @@ init_per_suite(Config) ->
{ok,Data} ->
TstDir = filename:join(proplists:get_value(priv_dir,Config), "test"),
file:make_dir(TstDir),
- make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)),
+ %% make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)),
+ ftp_test_lib:make_cert_files(proplists:get_value(data_dir,Config)),
start_ftpd([{test_dir,TstDir},
{ftpd_data,Data}
| Config])
@@ -204,17 +209,43 @@ init_per_group(Group, Config) when Group == ftps_active,
_:_ ->
{skip, "Crypto did not start"}
end;
-
+init_per_group(ftp_sup, Config) ->
+ try ftp:start() of
+ ok ->
+ Config
+ catch
+ _:_ ->
+ {skip, "Ftp did not start"}
+ end;
init_per_group(_Group, Config) ->
Config.
+
+end_per_group(ftp_sup, Config) ->
+ ftp:stop(),
+ Config;
end_per_group(_Group, Config) ->
Config.
%%--------------------------------------------------------------------
+init_per_testcase(T, Config0) when T =:= app; T =:= appup ->
+ Config0;
init_per_testcase(Case, Config0) ->
Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config0)),
- TLS = [{tls,[{reuse_sessions,true}]}],
+
+ %% Workaround for interoperability issues with vsftpd =< 3.0.2:
+ %%
+ %% vsftpd =< 3.0.2 does not support ECDHE ciphers and the ssl application
+ %% removed ciphers with RSA key exchange from its default cipher list.
+ %% To allow interoperability with old versions of vsftpd, cipher suites
+ %% with RSA key exchange are appended to the default cipher list.
+ All = ssl:cipher_suites(all, 'tlsv1.2'),
+ Default = ssl:cipher_suites(default, 'tlsv1.2'),
+ RSASuites =
+ ssl:filter_cipher_suites(All, [{key_exchange, fun(rsa) -> true;
+ (_) -> false end}]),
+ Suites = ssl:append_cipher_suites(RSASuites, Default),
+ TLS = [{tls,[{reuse_sessions,true},{ciphers, Suites}]}],
ACTIVE = [{mode,active}],
PASSIVE = [{mode,passive}],
CaseOpts = case Case of
@@ -229,6 +260,7 @@ init_per_testcase(Case, Config0) ->
ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ ExtraOpts);
ftp_passive -> ftp__open(Config0, PASSIVE ++ ExtraOpts);
ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ ExtraOpts);
+ ftp_sup -> ftp_start_service(Config0, ACTIVE ++ ExtraOpts);
undefined -> Config0
end,
case Case of
@@ -244,7 +276,7 @@ init_per_testcase(Case, Config0) ->
Config
end.
-
+end_per_testcase(T, _Config) when T =:= app; T =:= appup -> ok;
end_per_testcase(user, _Config) -> ok;
end_per_testcase(bad_user, _Config) -> ok;
end_per_testcase(error_elogin, _Config) -> ok;
@@ -261,11 +293,30 @@ end_per_testcase(_Case, Config) ->
_:_ -> ok
end
end,
- ftp__close(Config).
+ Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config)),
+ case Group of
+ ftp_sup ->
+ ftp_stop_service(Config);
+ _Else ->
+ ftp__close(Config)
+ end.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the ftp app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(ftp).
+
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the ftp appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(ftp).
+
+%%--------------------------------------------------------------------
+
user() -> [
{doc, "Open an ftp connection to a host, and logon as anonymous ftp,"
" then logoff"}].
@@ -834,6 +885,43 @@ clean_shutdown(Config) ->
end
end.
+%%-------------------------------------------------------------------------
+start_ftp() ->
+ [{doc, "Start/stop of ftp service"}].
+start_ftp(Config) ->
+ Pid0 = proplists:get_value(ftp,Config),
+ Pids0 = [ServicePid || {_, ServicePid} <- ftp:services()],
+ true = lists:member(Pid0, Pids0),
+ {ok, [_|_]} = ftp:service_info(Pid0),
+ ftp:stop_service(Pid0),
+ ct:sleep(100),
+ Pids1 = [ServicePid || {_, ServicePid} <- ftp:services()],
+ false = lists:member(Pid0, Pids1),
+
+ Host = proplists:get_value(ftpd_host,Config),
+ Port = proplists:get_value(ftpd_port,Config),
+
+ {ok, Pid1} = ftp:start_standalone([{host, Host},{port, Port}]),
+ Pids2 = [ServicePid || {_, ServicePid} <- ftp:services()],
+ false = lists:member(Pid1, Pids2).
+
+%%-------------------------------------------------------------------------
+ftp_worker() ->
+ [{doc, "Makes sure the ftp worker processes are added and removed "
+ "appropriatly to/from the supervison tree."}].
+ftp_worker(Config) ->
+ Pid = proplists:get_value(ftp,Config),
+ case supervisor:which_children(ftp_sup) of
+ [{_,_, worker, [ftp]}] ->
+ ftp:stop_service(Pid),
+ ct:sleep(5000),
+ [] = supervisor:which_children(ftp_sup),
+ ok;
+ Children ->
+ ct:fail("Unexpected children: ~p",[Children])
+ end.
+
+
%%%----------------------------------------------------------------
%%% Error codes not tested elsewhere
@@ -867,22 +955,6 @@ error_ehost(_Config) ->
%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
-make_cert_files(Alg1, Alg2, Prefix, Dir) ->
- CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg1}]),
- {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg2},{issuer,CaInfo}]),
- CaCertFile = filename:join(Dir, Prefix++"cacerts.pem"),
- CertFile = filename:join(Dir, Prefix++"cert.pem"),
- KeyFile = filename:join(Dir, Prefix++"key.pem"),
- der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
- der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
- der_to_pem(KeyFile, [CertKey]),
- ok.
-
-der_to_pem(File, Entries) ->
- PemBin = public_key:pem_encode(Entries),
- file:write_file(File, PemBin).
-
-%%--------------------------------------------------------------------
chk_file(Path=[C|_], ExpectedContents, Config) when 0<C,C=<255 ->
chk_file([Path], ExpectedContents, Config);
@@ -1029,6 +1101,17 @@ ftp__close(Config) ->
ok = ftp:close(proplists:get_value(ftp,Config)),
Config.
+ftp_start_service(Config, Options) ->
+ Host = proplists:get_value(ftpd_host,Config),
+ Port = proplists:get_value(ftpd_port,Config),
+ ct:log("Host=~p, Port=~p",[Host,Port]),
+ {ok,Pid} = ftp:start_service([{host, Host},{port,Port} | Options]),
+ [{ftp,Pid}|Config].
+
+ftp_stop_service(Config) ->
+ ok = ftp:stop_service(proplists:get_value(ftp,Config)),
+ Config.
+
split(Cs) -> string:tokens(Cs, "\r\n").
find_diff(Bin1, Bin2) ->
diff --git a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel b/lib/ftp/test/ftp_SUITE_data/ftpd_hosts.skel
index 75096ce687..75096ce687 100644
--- a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
+++ b/lib/ftp/test/ftp_SUITE_data/ftpd_hosts.skel
diff --git a/lib/ftp/test/ftp_SUITE_data/vsftpd.conf b/lib/ftp/test/ftp_SUITE_data/vsftpd.conf
new file mode 100644
index 0000000000..4568fad147
--- /dev/null
+++ b/lib/ftp/test/ftp_SUITE_data/vsftpd.conf
@@ -0,0 +1,33 @@
+
+###
+### Some parameters are given in the vsftpd start command.
+###
+### Typical command-line paramters are such that has a file path
+### component like cert files.
+###
+
+
+listen=YES
+listen_port=9999
+run_as_launching_user=YES
+ssl_enable=YES
+ssl_ciphers=HIGH:!aNULL:!MD5
+allow_anon_ssl=YES
+
+background=YES
+
+write_enable=YES
+anonymous_enable=YES
+anon_upload_enable=YES
+anon_mkdir_write_enable=YES
+anon_other_write_enable=YES
+anon_world_readable_only=NO
+
+### Shouldn't be necessary....
+require_ssl_reuse=NO
+
+### Logging
+#vsftpd_log_file=/devel/otp/vsftpd.log
+#xferlog_enable=YES
+#xferlog_std_format=NO
+#log_ftp_protocol=YES \ No newline at end of file
diff --git a/lib/ftp/test/ftp_bench.spec b/lib/ftp/test/ftp_bench.spec
new file mode 100644
index 0000000000..4d1ecf8891
--- /dev/null
+++ b/lib/ftp/test/ftp_bench.spec
@@ -0,0 +1 @@
+{suites,"../ftp_test",[]}.
diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/ftp/test/ftp_format_SUITE.erl
index 95d594a44b..95d594a44b 100644
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ b/lib/ftp/test/ftp_format_SUITE.erl
diff --git a/lib/ftp/test/ftp_internal.hrl b/lib/ftp/test/ftp_internal.hrl
new file mode 120000
index 0000000000..2ae5c46460
--- /dev/null
+++ b/lib/ftp/test/ftp_internal.hrl
@@ -0,0 +1 @@
+../src/ftp_internal.hrl \ No newline at end of file
diff --git a/lib/inets/test/ftp_property_test_SUITE.erl b/lib/ftp/test/ftp_property_test_SUITE.erl
index b314882296..46ed6959a8 100644
--- a/lib/inets/test/ftp_property_test_SUITE.erl
+++ b/lib/ftp/test/ftp_property_test_SUITE.erl
@@ -41,9 +41,11 @@ all() -> [prop_ftp_case].
init_per_suite(Config) ->
- inets:start(),
+ ftp:start(),
ct_property_test:init_per_suite(Config).
+end_per_suite(Config) ->
+ Config.
%%%---- test case
prop_ftp_case(Config) ->
diff --git a/lib/ftp/test/ftp_test_lib.erl b/lib/ftp/test/ftp_test_lib.erl
new file mode 100644
index 0000000000..f5fbc39037
--- /dev/null
+++ b/lib/ftp/test/ftp_test_lib.erl
@@ -0,0 +1,126 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(ftp_test_lib).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-export([make_cert_files/1]).
+
+
+make_cert_files(Dir) ->
+ #{server_config := ServerConf,
+ client_config := _} =
+ public_key:pkix_test_data(#{server_chain =>
+ #{root => [{key, hardcode_rsa_key(1)}],
+ intermediates => [[{key, hardcode_rsa_key(2)}]],
+ peer => [{key, hardcode_rsa_key(3)}]},
+ client_chain =>
+ #{root => [{key, hardcode_rsa_key(1)}],
+ intermediates => [[{key, hardcode_rsa_key(3)}]],
+ peer => [{key, hardcode_rsa_key(2)}]}}),
+
+ CaCertFile = filename:join(Dir, "server-cacerts.pem"),
+ CertFile = filename:join(Dir, "server-cert.pem"),
+ KeyFile = filename:join(Dir, "server-key.pem"),
+
+ CAs = proplists:get_value(cacerts, ServerConf),
+ Cert = proplists:get_value(cert, ServerConf),
+ Key = proplists:get_value(key, ServerConf),
+ der_to_pem(CertFile, [cert_entry(Cert)]),
+ der_to_pem(KeyFile, [key_entry(Key)]),
+ der_to_pem(CaCertFile, ca_entries(CAs)).
+
+cert_entry(Cert) ->
+ {'Certificate', Cert, not_encrypted}.
+
+key_entry({'RSAPrivateKey', DERKey}) ->
+ {'RSAPrivateKey', DERKey, not_encrypted};
+key_entry({'DSAPrivateKey', DERKey}) ->
+ {'DSAPrivateKey', DERKey, not_encrypted};
+key_entry({'ECPrivateKey', DERKey}) ->
+ {'ECPrivateKey', DERKey, not_encrypted}.
+
+ca_entries(CAs) ->
+ [{'Certificate', CACert, not_encrypted} || CACert <- CAs].
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
+
+hardcode_rsa_key(1) ->
+ #'RSAPrivateKey'{
+ version = 'two-prime',
+ modulus =
+23995666614853919027835084074500048897452890537492185072956789802729257783422306095699263934587064480357348855732149402060270996295002843755712064937715826848741191927820899197493902093529581182351132392364214171173881547273475904587683433713767834856230531387991145055273426806331200574039205571401702219159773947658558490957010003143162250693492642996408861265758000254664396313741422909188635443907373976005987612936763564996605457102336549804831742940035613780926178523017685712710473543251580072875247250504243621640157403744718833162626193206685233710319205099867303242759099560438381385658382486042995679707669,
+ publicExponent = 17,
+ privateExponent =
+11292078406990079542510627799764728892919007311761028269626724613049062486316379339152594792746853873109340637991599718616598115903530750002688030558925094987642913848386305504703012749896273497577003478759630198199473669305165131570674557041773098755873191241407597673069847908861741446606684974777271632545629600685952292605647052193819136445675100211504432575554351515262198132231537860917084269870590492135731720141577986787033006338680118008484613510063003323516659048210893001173583018220214626635609151105287049126443102976056146630518124476470236027123782297108342869049542023328584384300970694412006494684657,
+prime1 =
+169371138592582642967021557955633494538845517070305333860805485424261447791289944610138334410987654265476540480228705481960508520379619587635662291973699651583489223555422528867090299996446070521801757353675026048850480903160224210802452555900007597342687137394192939372218903554801584969667104937092080815197,
+ prime2 =
+141675062317286527042995673340952251894209529891636708844197799307963834958115010129693036021381525952081167155681637592199810112261679449166276939178032066869788822014115556349519329537177920752776047051833616197615329017439297361972726138285974555338480581117881706656603857310337984049152655480389797687577,
+ exponent1 =
+119556097830058336212015217380447172615655659108450823901745048534772786676204666783627059584226579481512852103690850928442711896738555003036938088452023283470698275450886490965004917644550167427154181661417665446247398284583687678213495921811770068712485038160606780733330990744565824684470897602653233516609,
+ exponent2 =
+41669135975672507953822256864985956439473391144599032012999352737636422046504414744027363535700448809435637398729893409470532385959317485048904982111185902020526124121798693043976273393287623750816484427009887116945685005129205106462566511260580751570141347387612266663707016855981760014456663376585234613993,
+ coefficient =
+76837684977089699359024365285678488693966186052769523357232308621548155587515525857011429902602352279058920284048929101483304120686557782043616693940283344235057989514310975192908256494992960578961614059245280827077951132083993754797053182279229469590276271658395444955906108899267024101096069475145863928441,
+ otherPrimeInfos = asn1_NOVALUE};
+
+hardcode_rsa_key(2) ->
+ #'RSAPrivateKey'{
+ version = 'two-prime',
+ modulus =
+21343679768589700771839799834197557895311746244621307033143551583788179817796325695589283169969489517156931770973490560582341832744966317712674900833543896521418422508485833901274928542544381247956820115082240721897193055368570146764204557110415281995205343662628196075590438954399631753508888358737971039058298703003743872818150364935790613286541190842600031570570099801682794056444451081563070538409720109449780410837763602317050353477918147758267825417201591905091231778937606362076129350476690460157227101296599527319242747999737801698427160817755293383890373574621116766934110792127739174475029121017282777887777,
+ publicExponent = 17,
+ privateExponent =
+18832658619343853622211588088997845201745658451136447382185486691577805721584993260814073385267196632785528033211903435807948675951440868570007265441362261636545666919252206383477878125774454042314841278013741813438699754736973658909592256273895837054592950290554290654932740253882028017801960316533503857992358685308186680144968293076156011747178275038098868263178095174694099811498968993700538293188879611375604635940554394589807673542938082281934965292051746326331046224291377703201248790910007232374006151098976879987912446997911775904329728563222485791845480864283470332826504617837402078265424772379987120023773,
+ prime1 =
+146807662748886761089048448970170315054939768171908279335181627815919052012991509112344782731265837727551849787333310044397991034789843793140419387740928103541736452627413492093463231242466386868459637115999163097726153692593711599245170083315894262154838974616739452594203727376460632750934355508361223110419,
+ prime2 =
+145385325050081892763917667176962991350872697916072592966410309213561884732628046256782356731057378829876640317801978404203665761131810712267778698468684631707642938779964806354584156202882543264893826268426566901882487709510744074274965029453915224310656287149777603803201831202222853023280023478269485417083,
+ exponent1 =
+51814469205489445090252393754177758254684624060673510353593515699736136004585238510239335081623236845018299924941168250963996835808180162284853901555621683602965806809675350150634081614988136541809283687999704622726877773856604093851236499993845033701707873394143336209718962603456693912094478414715725803677,
+ exponent2 =
+51312467664734785681382706062457526359131540440966797517556579722433606376221663384746714140373192528191755406283051201483646739222992016094510128871300458249756331334105225772206172777487956446433115153562317730076172132768497908567634716277852432109643395464627389577600646306666889302334125933506877206029,
+ coefficient =
+30504662229874176232343608562807118278893368758027179776313787938167236952567905398252901545019583024374163153775359371298239336609182249464886717948407152570850677549297935773605431024166978281486607154204888016179709037883348099374995148481968169438302456074511782717758301581202874062062542434218011141540,
+ otherPrimeInfos = asn1_NOVALUE};
+
+hardcode_rsa_key(3) ->
+ #'RSAPrivateKey'{
+ version = 'two-prime',
+ modulus =
+25089040456112869869472694987833070928503703615633809313972554887193090845137746668197820419383804666271752525807484521370419854590682661809972833718476098189250708650325307850184923546875260207894844301992963978994451844985784504212035958130279304082438876764367292331581532569155681984449177635856426023931875082020262146075451989132180409962870105455517050416234175675478291534563995772675388370042873175344937421148321291640477650173765084699931690748536036544188863178325887393475703801759010864779559318631816411493486934507417755306337476945299570726975433250753415110141783026008347194577506976486290259135429,
+ publicExponent = 17,
+ privateExponent =
+8854955455098659953931539407470495621824836570223697404931489960185796768872145882893348383311931058684147950284994536954265831032005645344696294253579799360912014817761873358888796545955974191021709753644575521998041827642041589721895044045980930852625485916835514940558187965584358347452650930302268008446431977397918214293502821599497633970075862760001650736520566952260001423171553461362588848929781360590057040212831994258783694027013289053834376791974167294527043946669963760259975273650548116897900664646809242902841107022557239712438496384819445301703021164043324282687280801738470244471443835900160721870265,
+ prime1 =
+171641816401041100605063917111691927706183918906535463031548413586331728772311589438043965564336865070070922328258143588739626712299625805650832695450270566547004154065267940032684307994238248203186986569945677705100224518137694769557564475390859269797990555863306972197736879644001860925483629009305104925823,
+ prime2
+=146170909759497809922264016492088453282310383272504533061020897155289106805616042710009332510822455269704884883705830985184223718261139908416790475825625309815234508695722132706422885088219618698987115562577878897003573425367881351537506046253616435685549396767356003663417208105346307649599145759863108910523,
+ exponent1 =
+60579464612132153154728441333538327425711971378777222246428851853999433684345266860486105493295364142377972586444050678378691780811632637288529186629507258781295583787741625893888579292084087601124818789392592131211843947578009918667375697196773859928702549128225990187436545756706539150170692591519448797349,
+ exponent2 =
+137572620950115585809189662580789132500998007785886619351549079675566218169991569609420548245479957900898715184664311515467504676010484619686391036071176762179044243478326713135456833024206699951987873470661533079532774988581535389682358631768109586527575902839864474036157372334443583670210960715165278974609,
+ coefficient =
+15068630434698373319269196003209754243798959461311186548759287649485250508074064775263867418602372588394608558985183294561315208336731894947137343239541687540387209051236354318837334154993136528453613256169847839789803932725339395739618592522865156272771578671216082079933457043120923342632744996962853951612,
+ otherPrimeInfos = asn1_NOVALUE}.
diff --git a/lib/ftp/test/property_test/README b/lib/ftp/test/property_test/README
new file mode 100644
index 0000000000..57602bf719
--- /dev/null
+++ b/lib/ftp/test/property_test/README
@@ -0,0 +1,12 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The test in this directory are written assuming that the user has a QuickCheck license. They are to be run manually. Some may be possible to be run with other tools, e.g. PropEr.
+
diff --git a/lib/ftp/test/property_test/ftp_simple_client_server.erl b/lib/ftp/test/property_test/ftp_simple_client_server.erl
new file mode 100644
index 0000000000..1bc54128f6
--- /dev/null
+++ b/lib/ftp/test/property_test/ftp_simple_client_server.erl
@@ -0,0 +1,307 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ftp_simple_client_server).
+
+-compile(export_all).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-define(EQC,true).
+%%-define(PROPER,true).
+-endif.
+-endif.
+
+
+-ifdef(EQC).
+
+-include_lib("eqc/include/eqc.hrl").
+-include_lib("eqc/include/eqc_statem.hrl").
+-define(MOD_eqc, eqc).
+-define(MOD_eqc_gen, eqc_gen).
+-define(MOD_eqc_statem, eqc_statem).
+
+-else.
+-ifdef(PROPER).
+
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc, proper).
+-define(MOD_eqc_gen, proper_gen).
+-define(MOD_eqc_statem, proper_statem).
+
+-endif.
+-endif.
+
+-record(state, {
+ initialized = false,
+ priv_dir,
+ data_dir,
+ servers = [], % [ {IP,Port,Userid,Pwd} ]
+ clients = [], % [ client_ref() ]
+ store = [] % [ {Name,Contents} ]
+ }).
+
+-define(fmt(F,A), io:format(F,A)).
+%%-define(fmt(F,A), ok).
+
+-define(v(K,L), proplists:get_value(K,L)).
+
+%%%================================================================
+%%%
+%%% Properties
+%%%
+
+%% This function is for normal eqc calls:
+prop_ftp() ->
+ {ok,PWD} = file:get_cwd(),
+ prop_ftp(filename:join([PWD,?MODULE_STRING++"_data"]),
+ filename:join([PWD,?MODULE_STRING,"_files"])).
+
+%% This function is for calls from common_test test cases:
+prop_ftp(Config) ->
+ prop_ftp(filename:join([?v(property_dir,Config), ?MODULE_STRING++"_data"]),
+ ?v(priv_dir,Config) ).
+
+
+prop_ftp(DataDir, PrivDir) ->
+ S0 = #state{data_dir = DataDir,
+ priv_dir = PrivDir},
+ ?FORALL(Cmds, more_commands(10,commands(?MODULE,S0)),
+ aggregate(command_names(Cmds),
+ begin {_H,S,Result} = run_commands(?MODULE,Cmds),
+ % io:format('**** Result=~p~n',[Result]),
+ % io:format('**** S=~p~n',[S]),
+ % io:format('**** _H=~p~n',[_H]),
+ % io:format('**** Cmds=~p~n',[Cmds]),
+ [cmnd_stop_server(X) || X <- S#state.servers],
+ [ftp:stop_service(X) || {ok,X} <- S#state.clients],
+ Result==ok
+ end)
+ ).
+
+%%%================================================================
+%%%
+%%% State model
+%%%
+
+%% @doc Returns the state in which each test case starts. (Unless a different
+%% initial state is supplied explicitly to, e.g. commands/2.)
+-spec initial_state() ->?MOD_eqc_statem:symbolic_state().
+initial_state() ->
+ ?fmt("Initial_state()~n",[]),
+ #state{}.
+
+%% @doc Command generator, S is the current state
+-spec command(S :: ?MOD_eqc_statem:symbolic_state()) -> ?MOD_eqc_gen:gen(eqc_statem:call()).
+
+command(#state{initialized=false,
+ priv_dir=PrivDir}) ->
+ {call,?MODULE,cmnd_init,[PrivDir]};
+
+command(#state{servers=[],
+ priv_dir=PrivDir,
+ data_dir=DataDir}) ->
+ {call,?MODULE,cmnd_start_server,[PrivDir,DataDir]};
+
+command(#state{servers=Ss=[_|_],
+ clients=[]}) ->
+ {call,?MODULE,cmnd_start_client,[oneof(Ss)]};
+
+command(#state{servers=Ss=[_|_],
+ clients=Cs=[_|_],
+ store=Store=[_|_]
+ }) ->
+ frequency([
+ { 5, {call,?MODULE,cmnd_start_client,[oneof(Ss)]}},
+ { 5, {call,?MODULE,cmnd_stop_client,[oneof(Cs)]}},
+ {10, {call,?MODULE,cmnd_put,[oneof(Cs),file_path(),file_contents()]}},
+ {20, {call,?MODULE,cmnd_get,[oneof(Cs),oneof(Store)]}},
+ {10, {call,?MODULE,cmnd_delete,[oneof(Cs),oneof(Store)]}}
+ ]);
+
+command(#state{servers=Ss=[_|_],
+ clients=Cs=[_|_],
+ store=[]
+ }) ->
+ frequency([
+ {5, {call,?MODULE,cmnd_start_client,[oneof(Ss)]}},
+ {5, {call,?MODULE,cmnd_stop_client,[oneof(Cs)]}},
+ {10, {call,?MODULE,cmnd_put,[oneof(Cs),file_path(),file_contents()]}}
+ ]).
+
+%% @doc Precondition, checked before command is added to the command sequence.
+-spec precondition(S :: ?MOD_eqc_statem:symbolic_state(), C :: ?MOD_eqc_statem:call()) -> boolean().
+
+precondition(#state{clients=Cs}, {call, _, cmnd_put, [C,_,_]}) -> lists:member(C,Cs);
+
+precondition(#state{clients=Cs, store=Store},
+ {call, _, cmnd_get, [C,X]}) -> lists:member(C,Cs) andalso lists:member(X,Store);
+
+precondition(#state{clients=Cs, store=Store},
+ {call, _, cmnd_delete, [C,X]}) -> lists:member(C,Cs) andalso lists:member(X,Store);
+
+precondition(#state{servers=Ss}, {call, _, cmnd_start_client, _}) -> Ss =/= [];
+
+precondition(#state{clients=Cs}, {call, _, cmnd_stop_client, [C]}) -> lists:member(C,Cs);
+
+precondition(#state{initialized=IsInit}, {call, _, cmnd_init, _}) -> IsInit==false;
+
+precondition(_S, {call, _, _, _}) -> true.
+
+
+%% @doc Postcondition, checked after command has been evaluated
+%% Note: S is the state before next_state(S,_,C)
+-spec postcondition(S :: ?MOD_eqc_statem:dynamic_state(), C :: ?MOD_eqc_statem:call(),
+ Res :: term()) -> boolean().
+
+postcondition(_S, {call, _, cmnd_get, [_,{_Name,Expected}]}, {ok,Value}) ->
+ Value == Expected;
+
+postcondition(S, {call, _, cmnd_delete, [_,{Name,_Expected}]}, ok) ->
+ ?fmt("file:read_file(..) = ~p~n",[file:read_file(filename:join(S#state.priv_dir,Name))]),
+ {error,enoent} == file:read_file(filename:join(S#state.priv_dir,Name));
+
+postcondition(S, {call, _, cmnd_put, [_,Name,Value]}, ok) ->
+ {ok,Bin} = file:read_file(filename:join(S#state.priv_dir,Name)),
+ Bin == unicode:characters_to_binary(Value);
+
+postcondition(_S, {call, _, cmnd_stop_client, _}, ok) -> true;
+
+postcondition(_S, {call, _, cmnd_start_client, _}, {ok,_}) -> true;
+
+postcondition(_S, {call, _, cmnd_init, _}, ok) -> true;
+
+postcondition(_S, {call, _, cmnd_start_server, _}, {ok,_}) -> true.
+
+
+%% @doc Next state transformation, S is the current state. Returns next state.
+-spec next_state(S :: ?MOD_eqc_statem:symbolic_state(),
+ V :: ?MOD_eqc_statem:var(),
+ C :: ?MOD_eqc_statem:call()) -> ?MOD_eqc_statem:symbolic_state().
+
+next_state(S, _V, {call, _, cmnd_put, [_,Name,Val]}) ->
+ S#state{store = [{Name,Val} | lists:keydelete(Name,1,S#state.store)]};
+
+next_state(S, _V, {call, _, cmnd_delete, [_,{Name,_Val}]}) ->
+ S#state{store = lists:keydelete(Name,1,S#state.store)};
+
+next_state(S, V, {call, _, cmnd_start_client, _}) ->
+ S#state{clients = [V | S#state.clients]};
+
+next_state(S, V, {call, _, cmnd_start_server, _}) ->
+ S#state{servers = [V | S#state.servers]};
+
+next_state(S, _V, {call, _, cmnd_stop_client, [C]}) ->
+ S#state{clients = S#state.clients -- [C]};
+
+next_state(S, _V, {call, _, cmnd_init, _}) ->
+ S#state{initialized=true};
+
+next_state(S, _V, {call, _, _, _}) ->
+ S.
+
+%%%================================================================
+%%%
+%%% Data model
+%%%
+
+file_path() -> non_empty(list(alphanum_char())).
+%%file_path() -> non_empty( list(oneof([alphanum_char(), utf8_char()])) ).
+
+%%file_contents() -> list(alphanum_char()).
+file_contents() -> list(oneof([alphanum_char(), utf8_char()])).
+
+alphanum_char() -> oneof(lists:seq($a,$z) ++ lists:seq($A,$Z) ++ lists:seq($0,$9)).
+
+utf8_char() -> oneof("åäöÅÄÖ話话カタカナひらがな").
+
+%%%================================================================
+%%%
+%%% Commands doing something with the System Under Test
+%%%
+
+cmnd_init(PrivDir) ->
+ ?fmt('Call cmnd_init(~p)~n',[PrivDir]),
+ os:cmd("killall vsftpd"),
+ clear_files(PrivDir),
+ ok.
+
+cmnd_start_server(PrivDir, DataDir) ->
+ ?fmt('Call cmnd_start_server(~p, ~p)~n',[PrivDir,DataDir]),
+ Cmnd = ["vsftpd ", filename:join(DataDir,"vsftpd.conf"),
+ " -oftpd_banner=erlang_otp_testing"
+ " -oanon_root=",PrivDir
+ ],
+ ?fmt("Cmnd=~s~n",[Cmnd]),
+ case os:cmd(Cmnd) of
+ [] ->
+ {ok,{"localhost",9999,"ftp","[email protected]"}};
+ Other ->
+ {error,Other}
+ end.
+
+cmnd_stop_server({ok,{_Host,Port,_Usr,_Pwd}}) ->
+ os:cmd("kill `netstat -tpln | grep "++integer_to_list(Port)++" | awk '{print $7}' | awk -F/ '{print $1}'`").
+
+cmnd_start_client({ok,{Host,Port,Usr,Pwd}}) ->
+ ?fmt('Call cmnd_start_client(~p)...',[{Host,Port,Usr,Pwd}]),
+ case ftp:start_service([{host,Host},{port,Port}]) of
+ {ok,Client} ->
+ ?fmt("~p...",[{ok,Client}]),
+ case ftp:user(Client, Usr, Pwd) of
+ ok ->
+ ?fmt("OK!~n",[]),
+ {ok,Client};
+ Other ->
+ ?fmt("Other1=~p~n",[Other]),
+ ftp:stop_service(Client), Other
+ end;
+ Other ->
+ ?fmt("Other2=~p~n",[Other]),
+ Other
+ end.
+
+cmnd_stop_client({ok,Client}) ->
+ ?fmt('Call cmnd_stop_client(~p)~n',[Client]),
+ ftp:stop_service(Client). %% -> ok | Other
+
+cmnd_delete({ok,Client}, {Name,_ExpectedValue}) ->
+ ?fmt('Call cmnd_delete(~p, ~p)~n',[Client,Name]),
+ R=ftp:delete(Client, Name),
+ ?fmt("R=~p~n",[R]),
+ R.
+
+cmnd_put({ok,Client}, Name, Value) ->
+ ?fmt('Call cmnd_put(~p, ~p, ~p)...',[Client, Name, Value]),
+ R = ftp:send_bin(Client, unicode:characters_to_binary(Value), Name), % ok | {error,Error}
+ ?fmt('~p~n',[R]),
+ R.
+
+cmnd_get({ok,Client}, {Name,_ExpectedValue}) ->
+ ?fmt('Call cmnd_get(~p, ~p)~n',[Client,Name]),
+ case ftp:recv_bin(Client, Name) of
+ {ok,Bin} -> {ok, unicode:characters_to_list(Bin)};
+ Other -> Other
+ end.
+
+
+clear_files(Dir) ->
+ os:cmd(["rm -fr ",filename:join(Dir,"*")]).
diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/ftp/test/property_test/ftp_simple_client_server_data/vsftpd.conf
index a5584f5916..fd48e2abf0 100644
--- a/lib/inets/test/ftp_SUITE_data/vsftpd.conf
+++ b/lib/ftp/test/property_test/ftp_simple_client_server_data/vsftpd.conf
@@ -10,8 +10,8 @@
listen=YES
listen_port=9999
run_as_launching_user=YES
-ssl_enable=YES
-allow_anon_ssl=YES
+ssl_enable=NO
+#allow_anon_ssl=YES
background=YES
diff --git a/lib/ftp/vsn.mk b/lib/ftp/vsn.mk
new file mode 100644
index 0000000000..3099144a6e
--- /dev/null
+++ b/lib/ftp/vsn.mk
@@ -0,0 +1,24 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2018. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+
+APPLICATION = ftp
+FTP_VSN = 1.0
+PRE_VSN =
+APP_VSN = "$(APPLICATION)-$(FTP_VSN)$(PRE_VSN)"
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index cbfa5c9e30..90c1258d4a 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -43,13 +43,10 @@ XML_CHAPTER_FILES = \
inets_services.xml \
http_client.xml \
http_server.xml \
- ftp_client.xml \
notes.xml
XML_REF3_FILES = \
inets.xml \
- ftp.xml \
- tftp.xml \
http_uri.xml\
httpc.xml\
httpd.xml \
diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml
index 137381cbe9..eb4e51584f 100644
--- a/lib/inets/doc/src/inets.xml
+++ b/lib/inets/doc/src/inets.xml
@@ -188,10 +188,9 @@
<section>
<title>SEE ALSO</title>
- <p><seealso marker="ftp">ftp(3)</seealso>,
- <seealso marker="httpc">httpc(3)</seealso>,
- <seealso marker="httpd">httpd(3)</seealso>,
- <seealso marker="tftp">tftp(3)</seealso></p>
+ <p><seealso marker="httpc">httpc(3)</seealso>,
+ <seealso marker="httpd">httpd(3)</seealso>
+ </p>
</section>
</erlref>
diff --git a/lib/inets/doc/src/introduction.xml b/lib/inets/doc/src/introduction.xml
index 1af2ef5dae..faf911f188 100644
--- a/lib/inets/doc/src/introduction.xml
+++ b/lib/inets/doc/src/introduction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,12 +22,12 @@
</legalnotice>
<title>Introduction</title>
- <prepared>Ingela Anderton Andin</prepared>
+ <prepared>Péter Dimitrov</prepared>
<responsible></responsible>
<docno></docno>
<approved></approved>
<checked></checked>
- <date>2004-09-28</date>
+ <date>2018-02-28</date>
<rev>A</rev>
<file>introduction.xml</file>
</header>
@@ -37,8 +37,6 @@
<p><c>Inets</c> is a container for Internet clients and servers
including the following:</p>
<list type="bulleted">
- <item>An FTP client</item>
- <item>A TFTP client and server</item>
<item>An <term id="HTTP"></term> client and server</item>
</list>
<p>The HTTP client and server are HTTP 1.1 compliant as
@@ -50,7 +48,7 @@
<title>Prerequisites</title>
<p>It is assumed that the reader is familiar with the Erlang
programming language, concepts of OTP, and has a basic
- understanding of the FTP, TFTP, and HTTP protocols.</p>
+ understanding of and HTTP protocol.</p>
</section>
</chapter>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 0417e07de8..ca37a54691 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -1766,7 +1766,7 @@
<list>
<item>
<p>[ftpc] Add a config option to specify a
- <seealso marker="ftp#dtimeout">data connect timeout</seealso>.
+ <seealso marker="ftp:ftp#dtimeout">data connect timeout</seealso>.
That is how long the ftp client will wait for the server to connect
to the data socket. If this timeout occurs, an error will be
returned to the caller and the ftp client process will be
@@ -2649,10 +2649,10 @@
<item>
<p>It is now also possible to start a standalone FTP client
process using the re-introduced
- <seealso marker="ftp#open">ftp:open</seealso>
+ <seealso marker="ftp:ftp#open">ftp:open</seealso>
function. </p>
<p>This is an alternative to starting the client using the
- <seealso marker="ftp#service_start">inets service framework</seealso>. </p>
+ <seealso marker="ftp:ftp#service_start">inets service framework</seealso>. </p>
<p>The old <c>ftp:open/1</c>, undocumented, function,
caused the client to be hooken into the inets service
supervision framework. This is <em>no</em> longer the
@@ -2665,10 +2665,10 @@
flag), and only used IPv4 if this did not work.
This has now been <em>changed</em>. </p>
<p>A new option,
- <seealso marker="ftp#ipfamily">ipfamily</seealso>,
+ <seealso marker="ftp:ftp#ipfamily">ipfamily</seealso>,
has been introduced, with the default value
<c>inet</c> (IPv4). </p>
- <p>See <seealso marker="ftp#open">ftp:open</seealso>
+ <p>See <seealso marker="ftp:ftp#open">ftp:open</seealso>
for more info.</p>
<p>*** POTENTIAL INCOMPATIBILITY ***</p>
</item>
@@ -2702,9 +2702,9 @@
<item>
<p>[ftpc] - The
- <seealso marker="ftp#ls2">ls/2</seealso> function (LIST command)
+ <seealso marker="ftp:ftp#ls2">ls/2</seealso> function (LIST command)
and the
- <seealso marker="ftp#nlist2">nlist/2</seealso> function
+ <seealso marker="ftp:ftp#nlist2">nlist/2</seealso> function
(NLST command)
with wildcards did
not work properly. </p>
diff --git a/lib/inets/doc/src/part.xml b/lib/inets/doc/src/part.xml
index f777481b5c..b9c8ed674c 100644
--- a/lib/inets/doc/src/part.xml
+++ b/lib/inets/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -23,9 +23,9 @@
</legalnotice>
<title>Inets User's Guide</title>
- <prepared>Ingela Anderton Andin</prepared>
+ <prepared>Péter Dimitrov</prepared>
<docno></docno>
- <date>2002-09-17</date>
+ <date>2018-02-28</date>
<rev>A</rev>
<file>part.sgml</file>
</header>
@@ -33,8 +33,6 @@
<p>The <c>Inets</c> application provides a set of
Internet-related services as follows:</p>
<list type="bulleted">
- <item>An FTP client</item>
- <item>A TFTP client and server</item>
<item>An <term id="HTTP"></term> client and server</item>
</list>
<p>The HTTP client and server are HTTP 1.1 compliant as
@@ -43,7 +41,6 @@
</description>
<xi:include href="introduction.xml"/>
<xi:include href="inets_services.xml"/>
- <xi:include href="ftp_client.xml"/>
<xi:include href="http_client.xml"/>
<xi:include href="http_server.xml"/>
</part>
diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml
index 27021ea09a..58c1a651f9 100644
--- a/lib/inets/doc/src/ref_man.xml
+++ b/lib/inets/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2015</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -23,20 +23,16 @@
</legalnotice>
<title>Inets Reference Manual</title>
- <prepared>Joakim Greben&ouml;</prepared>
+ <prepared>Péter Dimitrov</prepared>
<docno></docno>
- <date>1997-07-16</date>
+ <date>2018-02-28</date>
<rev>2.1</rev>
<file>ref_man.xml</file>
</header>
<description>
- <p><c>Inets</c> is a container for Internet clients and
- servers. An FTP client, an HTTP client and server, and
- a TFTP client and server are incorporated in <c>Inets</c>.</p>
+ <p><c>Inets</c> is a container for an HTTP client and server.</p>
</description>
<xi:include href="inets.xml"/>
- <xi:include href="ftp.xml"/>
- <xi:include href="tftp.xml"/>
<xi:include href="httpc.xml"/>
<xi:include href="httpd.xml"/>
<xi:include href="httpd_custom_api.xml"/>
diff --git a/lib/inets/src/ftp/ftp_sup.erl b/lib/inets/src/ftp/ftp_sup.erl
deleted file mode 100644
index 21dcfb6ab2..0000000000
--- a/lib/inets/src/ftp/ftp_sup.erl
+++ /dev/null
@@ -1,60 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The top supervisor for the ftp hangs under inets_sup.
-%%----------------------------------------------------------------------
--module(ftp_sup).
-
--behaviour(supervisor).
-
-%% API
--export([start_link/0]).
--export([start_child/1]).
-
-%% Supervisor callback
--export([init/1]).
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-start_link() ->
- supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-
-start_child(Args) ->
- supervisor:start_child(?MODULE, Args).
-
-%%%=========================================================================
-%%% Supervisor callback
-%%%=========================================================================
-init(_) ->
- RestartStrategy = simple_one_for_one,
- MaxR = 0,
- MaxT = 3600,
-
- Name = undefined, % As simple_one_for_one is used.
- StartFunc = {ftp, start_link, []},
- Restart = temporary, % E.g. should not be restarted
- Shutdown = 4000,
- Modules = [ftp],
- Type = worker,
-
- ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
- {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index eb0098dbee..fad2fefe2f 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -48,7 +48,9 @@ MODULES = \
inets_app \
inets_sup \
inets_trace \
- inets_lib
+ inets_lib \
+ inets_ftp_wrapper \
+ inets_tftp_wrapper
INTERNAL_HRL_FILES = inets_internal.hrl
EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index eb4be932ac..dfe773e7fe 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -30,10 +30,7 @@
inets_lib,
%% FTP
- ftp,
- ftp_progress,
- ftp_response,
- ftp_sup,
+ inets_ftp_wrapper,
%% HTTP client:
httpc,
@@ -101,13 +98,7 @@
mod_trace,
%% TFTP
- tftp,
- tftp_binary,
- tftp_engine,
- tftp_file,
- tftp_lib,
- tftp_logger,
- tftp_sup
+ inets_tftp_wrapper
]},
{registered,[inets_sup, httpc_manager]},
%% If the "new" ssl is used then 'crypto' must be started before inets.
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl
index 2d380012d7..450adf1a02 100644
--- a/lib/inets/src/inets_app/inets.erl
+++ b/lib/inets/src/inets_app/inets.erl
@@ -465,13 +465,19 @@ call_service(Service, Call, Args) ->
exit:{noproc, _} ->
{error, inets_not_started}
end.
-
+
+%% Obsolete! Kept for backward compatiblity!
+%% TFTP application has been moved out from inets
service_module(tftpd) ->
- tftp;
+ inets_tftp_wrapper;
service_module(tftpc) ->
- tftp;
+ inets_tftp_wrapper;
+service_module(tftp) ->
+ inets_tftp_wrapper;
+%% Obsolete! Kept for backward compatiblity!
+%% FTP application has been moved out from inets
service_module(ftpc) ->
- ftp;
+ inets_ftp_wrapper;
service_module(Service) ->
Service.
diff --git a/lib/inets/src/inets_app/inets_ftp_wrapper.erl b/lib/inets/src/inets_app/inets_ftp_wrapper.erl
new file mode 100644
index 0000000000..e350a490f7
--- /dev/null
+++ b/lib/inets/src/inets_app/inets_ftp_wrapper.erl
@@ -0,0 +1,48 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(inets_ftp_wrapper).
+
+
+-export([start_standalone/1,
+ start_service/1,
+ stop_service/1,
+ services/0,
+ service_info/1]).
+
+
+start_standalone(Options) ->
+ ftp:start_standalone(Options).
+
+
+start_service(Options) ->
+ application:ensure_started(ftp),
+ ftp:start_service(Options).
+
+
+stop_service(Pid) ->
+ ftp:stop_service(Pid).
+
+
+services() ->
+ [].
+
+
+service_info(_) ->
+ [].
diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl
index d8ae7eff26..22c928f9f9 100644
--- a/lib/inets/src/inets_app/inets_sup.erl
+++ b/lib/inets/src/inets_app/inets_sup.erl
@@ -61,19 +61,7 @@ children() ->
Services = get_services(),
HttpdServices = [Service || Service <- Services, is_httpd(Service)],
HttpcServices = [Service || Service <- Services, is_httpc(Service)],
- TftpdServices = [Service || Service <- Services, is_tftpd(Service)],
- [ftp_child_spec(), httpc_child_spec(HttpcServices),
- httpd_child_spec(HttpdServices), tftpd_child_spec(TftpdServices)].
-
-ftp_child_spec() ->
- Name = ftp_sup,
- StartFunc = {ftp_sup, start_link, []},
- Restart = permanent,
- Shutdown = infinity,
- Modules = [ftp_sup],
- Type = supervisor,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
+ [httpc_child_spec(HttpcServices), httpd_child_spec(HttpdServices)].
httpc_child_spec(HttpcServices0) ->
HttpcServices = default_profile(HttpcServices0, []),
@@ -94,15 +82,6 @@ httpd_child_spec(HttpdServices) ->
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-tftpd_child_spec(TftpServices) ->
- Name = tftp_sup,
- StartFunc = {tftp_sup, start_link, [TftpServices]},
- Restart = permanent,
- Shutdown = infinity,
- Modules = [tftp_sup],
- Type = supervisor,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
is_httpd({httpd, _}) ->
true;
is_httpd({httpd, _, _}) ->
@@ -115,11 +94,6 @@ is_httpc({httpc, _}) ->
is_httpc(_) ->
false.
-is_tftpd({tftpd, _}) ->
- true;
-is_tftpd(_) ->
- false.
-
default_profile([], Acc) ->
[{httpc, {default, only_session_cookies}} | Acc];
default_profile([{httpc, {default, _}} | _] = Profiles, Acc) ->
diff --git a/lib/inets/src/inets_app/inets_tftp_wrapper.erl b/lib/inets/src/inets_app/inets_tftp_wrapper.erl
new file mode 100644
index 0000000000..1e5deb234b
--- /dev/null
+++ b/lib/inets/src/inets_app/inets_tftp_wrapper.erl
@@ -0,0 +1,48 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(inets_tftp_wrapper).
+
+
+-export([start_standalone/1,
+ start_service/1,
+ stop_service/1,
+ services/0,
+ service_info/1]).
+
+
+start_standalone(Options) ->
+ tftp:start_standalone(Options).
+
+
+start_service(Options) ->
+ application:ensure_started(tftp),
+ tftp:start_service(Options).
+
+
+stop_service(Pid) ->
+ tftp:stop_service(Pid).
+
+
+services() ->
+ [].
+
+
+service_info(_) ->
+ [].
diff --git a/lib/inets/src/subdirs.mk b/lib/inets/src/subdirs.mk
index 9f2a0079f2..e9f4de959c 100644
--- a/lib/inets/src/subdirs.mk
+++ b/lib/inets/src/subdirs.mk
@@ -1,3 +1,3 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SUB_DIRECTORIES = inets_app http_lib http_client http_server ftp tftp
+SUB_DIRECTORIES = inets_app http_lib http_client http_server
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 99a7e6a9db..0e33b72095 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -44,8 +44,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
INCLUDES = -I. \
-I$(ERL_TOP)/lib/inets/src/inets_app \
-I$(ERL_TOP)/lib/inets/src/http_lib \
- -I$(ERL_TOP)/lib/inets/src/http_client \
- -I$(ERL_TOP)/lib/inets/src/ftp
+ -I$(ERL_TOP)/lib/inets/src/http_client
CP = cp
@@ -68,34 +67,6 @@ INETS_FLAGS = -Dinets_data_dir='"$(INETS_DATA_DIR)"' \
###
### test suite debug flags
###
-ifeq ($(FTP_DEBUG_CLIENT),)
- FTP_DEBUG_CLIENT = y
-endif
-
-ifeq ($(FTP_DEBUG_CLIENT),)
- FTP_FLAGS += -Dftp_debug_client
-endif
-
-ifeq ($(FTP_TRACE_CLIENT),)
- FTP_DEBUG_CLIENT = y
-endif
-
-ifeq ($(FTP_TRACE_CLIENT),y)
- FTP_FLAGS += -Dftp_trace_client
-endif
-
-ifneq ($(FTP_DEBUG),)
- FTP_DEBUG = s
-endif
-
-ifeq ($(FTP_DEBUG),l)
- FTP_FLAGS += -Dftp_log
-endif
-
-ifeq ($(FTP_DEBUG),d)
- FTP_FLAGS += -Dftp_debug -Dftp_log
-endif
-
ifeq ($(INETS_DEBUG),)
INETS_DEBUG = d
endif
@@ -151,8 +122,6 @@ MODULES = \
inets_test_lib \
erl_make_certs \
make_certs \
- ftp_SUITE \
- ftp_format_SUITE \
http_format_SUITE \
httpc_SUITE \
httpc_cookie_SUITE \
@@ -169,8 +138,6 @@ MODULES = \
httpd_test_lib \
inets_sup_SUITE \
inets_SUITE \
- tftp_test_lib \
- tftp_SUITE \
uri_SUITE \
inets_socketwrap_SUITE
@@ -179,10 +146,8 @@ EBIN = .
HRL_FILES = inets_test_lib.hrl \
inets_internal.hrl \
- ftp_internal.hrl \
httpc_internal.hrl \
- http_internal.hrl \
- tftp_test_lib.hrl
+ http_internal.hrl
ERL_FILES = $(MODULES:%=%.erl)
@@ -197,18 +162,15 @@ INETS_FILES = inets.config $(INETS_SPECS)
# SUB_SUITES = \
# inets_sup_suite \
# inets_httpd_suite \
-# inets_httpc_suite \
-# inets_ftp_suite \
-# inets_tftp_suite
+# inets_httpc_suite
INETS_DATADIRS = inets_SUITE_data inets_socketwrap_SUITE_data
HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data old_httpd_SUITE_data httpd_bench_SUITE_data
HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data
-FTP_DATADIRS = ftp_SUITE_data
-DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS) $(FTP_DATADIRS)
+DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS)
EMAKEFILE = Emakefile
MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
@@ -238,7 +200,6 @@ RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
# ----------------------------------------------------
ERL_COMPILE_FLAGS += \
$(INCLUDES) \
- $(FTP_FLAGS) \
$(INETS_FLAGS)
# ----------------------------------------------------
@@ -334,11 +295,3 @@ info:
@echo "INETS_PRIV_DIR = $(INETS_PRIV_DIR)"
@echo "INETS_ROOT = $(INETS_ROOT)"
@echo "INETS_FLAGS = $(INETS_FLAGS)"
- @echo "FTP_FLAGS = $(FTP_FLAGS)"
-
-tftp:
- erlc $(ERL_COMPILE_FLAGS) tftp_test_lib.erl tftp_SUITE.erl && erl -pa ../../inets/ebin -s tftp_SUITE t -s erlang halt
-
-tftp_work:
- echo "tftp_test_lib:t([{tftp_SUITE, all}])."
- erlc $(ERL_COMPILE_FLAGS) tftp_test_lib.erl tftp_SUITE.erl && erl -pa ../../inets/ebin
diff --git a/lib/inets/test/ftp_internal.hrl b/lib/inets/test/ftp_internal.hrl
deleted file mode 120000
index af57081f14..0000000000
--- a/lib/inets/test/ftp_internal.hrl
+++ /dev/null
@@ -1 +0,0 @@
-../src/ftp/ftp_internal.hrl \ No newline at end of file
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 1abd96a228..07ce594a1d 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -41,9 +41,7 @@ groups() ->
[{services_test, [],
[start_inets,
start_httpc,
- start_httpd,
- start_ftpc,
- start_tftpd
+ start_httpd
]},
{app_test, [], [app, appup]}].
@@ -298,79 +296,6 @@ start_httpd(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-start_ftpc(doc) ->
- [{doc, "Start/stop of ftpc service"}];
-start_ftpc(Config0) when is_list(Config0) ->
- process_flag(trap_exit, true),
- ok = inets:start(),
- case ftp_SUITE:init_per_suite(Config0) of
- {skip, _} = Skip ->
- Skip;
- Config ->
- FtpdHost = proplists:get_value(ftpd_host,Config),
- {ok, Pid0} = inets:start(ftpc, [{host, FtpdHost}]),
- Pids0 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- true = lists:member(Pid0, Pids0),
- [_|_] = inets:services_info(),
- inets:stop(ftpc, Pid0),
- ct:sleep(100),
- Pids1 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- false = lists:member(Pid0, Pids1),
- {ok, Pid1} =
- inets:start(ftpc, [{host, FtpdHost}], stand_alone),
- Pids2 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- false = lists:member(Pid1, Pids2),
- ok = inets:stop(stand_alone, Pid1),
- receive
- {'EXIT', Pid1, shutdown} ->
- ok
- after 100 ->
- ct:fail(stand_alone_not_shutdown)
- end,
- ok = inets:stop(),
- catch ftp_SUITE:end_per_SUITE(Config)
- end.
-
-%%-------------------------------------------------------------------------
-
-start_tftpd() ->
- [{doc, "Start/stop of tfpd service"}].
-start_tftpd(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ok = inets:start(),
- {ok, Pid0} = inets:start(tftpd, [{host, "localhost"}, {port, 0}]),
- Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
- true = lists:member(Pid0, Pids0),
- [_|_] = inets:services_info(),
- inets:stop(tftpd, Pid0),
- ct:sleep(100),
- Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
- false = lists:member(Pid0, Pids1),
- {ok, Pid1} =
- inets:start(tftpd, [{host, "localhost"}, {port, 0}], stand_alone),
- Pids2 = [ServicePid || {_, ServicePid} <- inets:services()],
- false = lists:member(Pid1, Pids2),
- ok = inets:stop(stand_alone, Pid1),
- receive
- {'EXIT', Pid1, shutdown} ->
- ok
- after 100 ->
- ct:fail(stand_alone_not_shutdown)
- end,
- ok = inets:stop(),
- application:load(inets),
- application:set_env(inets, services, [{tftpd,[{host, "localhost"},
- {port, 0}]}]),
- ok = inets:start(),
- (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- application:unset_env(inets, services),
- ok = inets:stop().
-
-%%-------------------------------------------------------------------------
-
httpd_reload() ->
[{doc, "Reload httpd configuration without restarting service"}].
httpd_reload(Config) when is_list(Config) ->
diff --git a/lib/inets/test/inets_socketwrap_SUITE.erl b/lib/inets/test/inets_socketwrap_SUITE.erl
index 7ea7e08ed1..fc87c595a9 100644
--- a/lib/inets/test/inets_socketwrap_SUITE.erl
+++ b/lib/inets/test/inets_socketwrap_SUITE.erl
@@ -30,7 +30,7 @@ suite() ->
[{ct_hooks,[ts_install_cth]}].
all() ->
- [start_httpd_fd, start_tftpd_fd].
+ [start_httpd_fd].
init_per_suite(Config) ->
case os:type() of
@@ -90,37 +90,7 @@ start_httpd_fd(Config) when is_list(Config) ->
ct:fail(open_port_failed)
end
end.
-%%-------------------------------------------------------------------------
-start_tftpd_fd() ->
- [{doc, "Start/stop of tfpd service with socket wrapper"}].
-start_tftpd_fd(Config) when is_list(Config) ->
- DataDir = proplists:get_value(data_dir, Config),
- case setup_node_info(node()) of
- {skip, _} = Skip ->
- Skip;
- {Node, NodeArg} ->
- InetPort = inets_test_lib:inet_port(node()),
- ct:pal("Node: ~p~n", [Node]),
- Wrapper = filename:join(DataDir, "setuid_socket_wrap"),
- Cmd = Wrapper ++
- " -s -tftpd_69,0:" ++ integer_to_list(InetPort)
- ++ " -p " ++ os:find_executable("erl") ++
- " -- " ++ NodeArg,
- ct:pal("cmd: ~p~n", [Cmd]),
- case open_port({spawn, Cmd}, [stderr_to_stdout]) of
- Port when is_port(Port) ->
- wait_node_up(Node, 10),
- ct:pal("~p", [rpc:call(Node, init, get_argument, [tftpd_69])]),
- ok = rpc:call(Node, inets, start, []),
- {ok, Pid} = rpc:call(Node, inets, start,
- [tftpd,[{host, "localhost"}]]),
- {ok, Info} = rpc:call(Node, tftp, info, [Pid]),
- {value,{port, InetPort}} = lists:keysearch(port, 1, Info),
- rpc:call(Node, erlang, halt, []);
- _ ->
- ct:fail(open_port_failed)
- end
- end.
+
%%-------------------------------------------------------------------------
%% Internal functions
%%-------------------------------------------------------------------------
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 1e664337e6..727e91e987 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -32,8 +32,7 @@ suite() ->
].
all() ->
- [default_tree, ftpc_worker, tftpd_worker,
- httpd_config, httpd_subtree, httpd_subtree_profile,
+ [default_tree, httpd_config, httpd_subtree, httpd_subtree_profile,
httpc_subtree].
groups() ->
@@ -147,15 +146,11 @@ default_tree() ->
"in the default case."}].
default_tree(Config) when is_list(Config) ->
TopSupChildren = supervisor:which_children(inets_sup),
- 4 = length(TopSupChildren),
+ 2 = length(TopSupChildren),
{value, {httpd_sup, _, supervisor,[httpd_sup]}} =
lists:keysearch(httpd_sup, 1, TopSupChildren),
{value, {httpc_sup, _,supervisor,[httpc_sup]}} =
lists:keysearch(httpc_sup, 1, TopSupChildren),
- {value, {ftp_sup,_,supervisor,[ftp_sup]}} =
- lists:keysearch(ftp_sup, 1, TopSupChildren),
- {value, {tftp_sup,_,supervisor,[tftp_sup]}} =
- lists:keysearch(tftp_sup, 1, TopSupChildren),
HttpcSupChildren = supervisor:which_children(httpc_sup),
{value, {httpc_profile_sup,_, supervisor, [httpc_profile_sup]}} =
@@ -163,8 +158,6 @@ default_tree(Config) when is_list(Config) ->
{value, {httpc_handler_sup,_, supervisor, [httpc_handler_sup]}} =
lists:keysearch(httpc_handler_sup, 1, HttpcSupChildren),
- [] = supervisor:which_children(ftp_sup),
-
[] = supervisor:which_children(httpd_sup),
%% Default profile
@@ -172,48 +165,7 @@ default_tree(Config) when is_list(Config) ->
= supervisor:which_children(httpc_profile_sup),
[] = supervisor:which_children(httpc_handler_sup),
-
- [] = supervisor:which_children(tftp_sup),
-
- ok.
-ftpc_worker() ->
- [{doc, "Makes sure the ftp worker processes are added and removed "
- "appropriatly to/from the supervison tree."}].
-ftpc_worker(Config0) when is_list(Config0) ->
- [] = supervisor:which_children(ftp_sup),
- case ftp_SUITE:init_per_suite(Config0) of
- {skip, _} = Skip ->
- Skip;
- Config ->
- FtpdHost = proplists:get_value(ftpd_host,Config),
- {ok, Pid} = inets:start(ftpc, [{host, FtpdHost}]),
- case supervisor:which_children(ftp_sup) of
- [{_,_, worker, [ftp]}] ->
- inets:stop(ftpc, Pid),
- ct:sleep(5000),
- [] = supervisor:which_children(ftp_sup),
- catch ftp_SUITE:end_per_SUITE(Config),
- ok;
- Children ->
- catch ftp_SUITE:end_per_SUITE(Config),
- exit({unexpected_children, Children})
- end
- end.
-
-tftpd_worker() ->
- [{doc, "Makes sure the tftp sub tree is correct."}].
-tftpd_worker(Config) when is_list(Config) ->
- [] = supervisor:which_children(tftp_sup),
- {ok, Pid0} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
- {port, 0}]),
- {ok, _Pid1} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
- {port, 0}], stand_alone),
-
- [{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup),
- inets:stop(tftpd, Pid0),
- ct:sleep(5000),
- [] = supervisor:which_children(tftp_sup),
ok.
httpd_config() ->
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index 14c543ee2b..68fa071090 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -90,8 +90,8 @@
<section>
<title>Other Storage Media</title>
- <p>The <seealso marker="inets:ftp"><c>ftp</c></seealso>
- module (Inets) normally accesses the tar file on disk using
+ <p>The <seealso marker="ftp:ftp"><c>ftp</c></seealso>
+ module normally accesses the tar file on disk using
the <seealso marker="kernel:file"><c>file</c></seealso> module.
When other needs arise, you can define your own low-level Erlang
functions to perform the writing and reading on the storage media;
diff --git a/lib/tftp/AUTHORS b/lib/tftp/AUTHORS
new file mode 100644
index 0000000000..71fc97c4e0
--- /dev/null
+++ b/lib/tftp/AUTHORS
@@ -0,0 +1,11 @@
+Original Authors:
+
+Håkan Mattsson - tftp
+
+Contributors:
+
+Ingela Anderton Andin
+Martin Gustafsson
+Johan Blom
+Torbjörn Törnkvist
+Joe Armstrong \ No newline at end of file
diff --git a/lib/tftp/Makefile b/lib/tftp/Makefile
new file mode 100644
index 0000000000..5c3ed52b28
--- /dev/null
+++ b/lib/tftp/Makefile
@@ -0,0 +1,78 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Macros
+# ----------------------------------------------------
+
+SUB_DIRECTORIES = src doc/src
+
+include vsn.mk
+VSN = $(TFTP_VSN)
+
+SPECIAL_TARGETS =
+
+DIA_PLT = ./priv/plt/$(APPLICATION).plt
+DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
+
+
+# ----------------------------------------------------
+# Default Subdir Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_subdir.mk
+
+.PHONY: info gclean dialyzer dialyzer_plt dclean
+
+info:
+ @echo "OS: $(OS)"
+ @echo "DOCB: $(DOCB)"
+ @echo ""
+ @echo "TFTP_VSN: $(TFTP_VSN)"
+ @echo "APP_VSN: $(APP_VSN)"
+ @echo ""
+ @echo "DIA_PLT: $(DIA_PLT)"
+ @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
+ @echo ""
+
+gclean:
+ git clean -fXd
+
+dclean:
+ rm -f $(DIA_PLT)
+ rm -f $(DIA_ANALYSIS)
+
+dialyzer_plt: $(DIA_PLT)
+
+$(DIA_PLT):
+ @echo "Building $(APPLICATION) plt file"
+ @dialyzer --build_plt \
+ --output_plt $@ \
+ -r ../$(APPLICATION)/ebin \
+ --output $(DIA_ANALYSIS) \
+ --verbose
+
+dialyzer: $(DIA_PLT)
+ @echo "Running dialyzer on $(APPLICATION)"
+ @dialyzer --plt $< \
+ ../$(APPLICATION)/ebin \
+ --verbose
diff --git a/lib/tftp/doc/html/.gitignore b/lib/tftp/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/tftp/doc/html/.gitignore
diff --git a/lib/tftp/doc/man3/.gitignore b/lib/tftp/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/tftp/doc/man3/.gitignore
diff --git a/lib/tftp/doc/man6/.gitignore b/lib/tftp/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/tftp/doc/man6/.gitignore
diff --git a/lib/tftp/doc/pdf/.gitignore b/lib/tftp/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/tftp/doc/pdf/.gitignore
diff --git a/lib/tftp/doc/src/Makefile b/lib/tftp/doc/src/Makefile
new file mode 100644
index 0000000000..a2fdcf6325
--- /dev/null
+++ b/lib/tftp/doc/src/Makefile
@@ -0,0 +1,154 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2018. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(TFTP_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+
+XML_CHAPTER_FILES = \
+ introduction.xml \
+ getting_started.xml \
+ notes.xml
+
+XML_REF3_FILES = \
+ tftp.xml
+
+XML_PART_FILES = \
+ usersguide.xml
+
+BOOK_FILES = book.xml
+
+XML_FILES = \
+ $(BOOK_FILES) \
+ $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) \
+ $(XML_REF6_FILES) \
+ $(XML_REF3_FILES) \
+ $(XML_APPLICATION_FILES)
+
+# GIF_FILES = tftp.gif
+
+
+# ----------------------------------------------------
+
+HTML_FILES = \
+ $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+EXTRA_FILES = \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+ldocs: local_docs
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+clean clean_docs: clean_html clean_man clean_pdf
+ rm -f errs core *~
+
+man: $(MAN3_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt:
+
+clean_pdf:
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+
+clean_html:
+ rm -rf $(TOP_HTML_FILES) $(HTMLDIR)/*
+
+clean_man:
+ rm -f $(MAN3_FILES)
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+
+release_spec:
+
+info:
+ @echo "GIF_FILES:\n$(GIF_FILES)"
+ @echo ""
+ @echo "EXTRA_FILES:\n$(EXTRA_FILES)"
+ @echo ""
+ @echo "HTML_FILES:\n$(HTML_FILES)"
+ @echo ""
+ @echo "TOP_HTML_FILES:\n$(TOP_HTML_FILES)"
+ @echo ""
+ @echo "XML_REF3_FILES:\n$(XML_REF3_FILES)"
+ @echo ""
+ @echo "XML_REF6_FILES:\n$(XML_REF6_FILES)"
+ @echo ""
+ @echo "XML_CHAPTER_FILES:\n$(XML_CHAPTER_FILES)"
+ @echo ""
diff --git a/lib/tftp/doc/src/book.xml b/lib/tftp/doc/src/book.xml
new file mode 100644
index 0000000000..c0b551d517
--- /dev/null
+++ b/lib/tftp/doc/src/book.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>1997</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>TFTP</title>
+ <prepared>Péter Dimitrov</prepared>
+ <docno></docno>
+ <date>2018-03-22</date>
+ <rev>1.0</rev>
+ <file>book.sgml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>TFTP</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="usersguide.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
diff --git a/lib/tftp/doc/src/getting_started.xml b/lib/tftp/doc/src/getting_started.xml
new file mode 100644
index 0000000000..9bce52dbe0
--- /dev/null
+++ b/lib/tftp/doc/src/getting_started.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year>
+ <year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>Getting Started</title>
+ <prepared></prepared>
+ <docno></docno>
+ <approved></approved>
+ <date></date>
+ <rev></rev>
+ <file>getting_started.xml</file>
+ </header>
+
+ <section>
+ <title>General Information</title>
+ <p>The <seealso marker="tftp#start/1">start/1</seealso> function starts
+ a daemon process listening for UDP packets on a port. When it
+ receives a request for read or write, it spawns a temporary server
+ process handling the transfer.</p>
+ <p>On the client side,
+ function <seealso marker="tftp#read_file/3">read_file/3</seealso>
+ and <seealso marker="tftp#write_file/3">write_file/3</seealso>
+ spawn a temporary client process establishing
+ contact with a TFTP daemon and perform the file transfer.</p>
+ <p><c>tftp</c> uses a callback module to handle the file
+ transfer. Two such callback modules are provided,
+ <c>tftp_binary</c> and <c>tftp_file</c>. See
+ <seealso marker="tftp#read_file/3">read_file/3</seealso> and
+ <seealso marker="tftp#write_file/3">write_file/3</seealso> for details.
+ You can also implement your own callback modules, see
+ <seealso marker="tftp#tftp_callback">CALLBACK FUNCTIONS</seealso>.
+ A callback module provided by
+ the user is registered using option <c>callback</c>, see
+ <seealso marker="tftp#options">DATA TYPES</seealso>.</p>
+ </section>
+
+ <section>
+ <title>Using the TFTP client and server</title>
+ <p>This is a simple example of starting the TFTP server and reading the content
+ of a sample file using the TFTP client.</p>
+
+ <p><em>Step 1.</em> Create a sample file to be used for the transfer:</p>
+ <code>
+ $ echo "Erlang/OTP 21" > file.txt
+ </code>
+
+ <p><em>Step 2.</em> Start the TFTP server:</p>
+ <code type="erl" >
+ 1> {ok, Pid} = tftp:start([{port, 19999}]).
+ <![CDATA[{ok,<0.65.0>}]]>
+ </code>
+
+ <p><em>Step 3.</em> Start the TFTP client (in another shell):</p>
+ <code type="erl" >
+ 1> tftp:read_file("file.txt", binary, [{port, 19999}]).
+ <![CDATA[{ok,<<"Erlang/OTP 21\n">>}]]>
+ </code>
+ </section>
+
+</chapter>
diff --git a/lib/tftp/doc/src/introduction.xml b/lib/tftp/doc/src/introduction.xml
new file mode 100644
index 0000000000..70761db0dc
--- /dev/null
+++ b/lib/tftp/doc/src/introduction.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared>Péter Dimitrov</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2018-03-22</date>
+ <rev>A</rev>
+ <file>introduction.xml</file>
+ </header>
+
+ <section>
+ <title>Purpose</title>
+ <p>The Trivial File Transfer Protocol or TFTP is a very simple protocol
+ used to transfer files.</p>
+ <p>It has been implemented on top of the User Datagram protocol (UDP) so
+ it may be used to move files between machines on different networks
+ implementing UDP. It is designed to be small and easy to implement.
+ Therefore, it lacks most of the features of a regular FTP. The only
+ thing it can do is read and write files (or mail) from/to a remote server.
+ It cannot list directories, and currently has no provisions for user
+ authentication.</p>
+ <p>The <c>tftp</c> application implements the following IETF standards:</p>
+ <list type="bulleted">
+ <item>RFC 1350, The TFTP Protocol (revision 2)</item>
+ <item>RFC 2347, TFTP Option Extension</item>
+ <item>RFC 2348, TFTP Blocksize Option</item>
+ <item>RFC 2349, TFTP Timeout Interval and Transfer Size Options</item>
+ </list>
+ <p>The only feature that not is implemented is the <c>netascii</c> transfer mode.</p>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang
+ programming language, concepts of OTP, and has a basic
+ understanding of the TFTP protocol.</p>
+ </section>
+</chapter>
diff --git a/lib/tftp/doc/src/notes.xml b/lib/tftp/doc/src/notes.xml
new file mode 100644
index 0000000000..3a4d97a008
--- /dev/null
+++ b/lib/tftp/doc/src/notes.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2002</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>TFTP Release Notes</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2018-03-22</date>
+ <rev>A</rev>
+ <file>notes.xml</file>
+ </header>
+
+ <section><title>TFTP 1.0</title>
+
+ <section><title>First released version</title>
+ <list>
+ <item>
+ <p>
+ Inets application was split into multiple smaller protocol specific applications.
+ The TFTP application is a standalone TFTP client and server with the same functionality as
+ TFTP in Inets.</p>
+ <p>
+ Own Id: OTP-14113</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+</chapter>
diff --git a/lib/tftp/doc/src/ref_man.xml b/lib/tftp/doc/src/ref_man.xml
new file mode 100644
index 0000000000..41a6cc6d52
--- /dev/null
+++ b/lib/tftp/doc/src/ref_man.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1997</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>TFTP Reference Manual</title>
+ <prepared>Péter Dimitrov</prepared>
+ <docno></docno>
+ <date>2018-03-22</date>
+ <rev>1.0</rev>
+ <file>ref_man.xml</file>
+ </header>
+ <description>
+ <p>The <c>TFTP</c> application.</p>
+ </description>
+ <xi:include href="tftp.xml"/>
+</application>
diff --git a/lib/inets/doc/src/tftp.xml b/lib/tftp/doc/src/tftp.xml
index 10398f5088..481e5446ad 100644
--- a/lib/inets/doc/src/tftp.xml
+++ b/lib/tftp/doc/src/tftp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2015</year>
+ <year>2006</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,57 +31,9 @@
<module>tftp</module>
<modulesummary>Trivial FTP.</modulesummary>
<description>
- <p>This is a complete implementation of the following IETF standards:</p>
- <list type="bulleted">
- <item>RFC 1350, The TFTP Protocol (revision 2)</item>
- <item>RFC 2347, TFTP Option Extension</item>
- <item>RFC 2348, TFTP Blocksize Option</item>
- <item>RFC 2349, TFTP Timeout Interval and Transfer Size Options</item>
- </list>
- <p>The only feature that not is implemented is
- the "netascii" transfer mode.</p>
- <p>The <seealso marker="#start/1">start/1</seealso> function starts
- a daemon process listening for UDP packets on a port. When it
- receives a request for read or write, it spawns a temporary server
- process handling the transfer.</p>
- <p>On the client side,
- function <seealso marker="#read_file/3">read_file/3</seealso>
- and <seealso marker="#write_file/3">write_file/3</seealso>
- spawn a temporary client process establishing
- contact with a TFTP daemon and perform the file transfer.</p>
- <p><c>tftp</c> uses a callback module to handle the file
- transfer. Two such callback modules are provided,
- <c>tftp_binary</c> and <c>tftp_file</c>. See
- <seealso marker="#read_file/3">read_file/3</seealso> and
- <seealso marker="#write_file/3">write_file/3</seealso> for details.
- You can also implement your own callback modules, see
- <seealso marker="#tftp_callback">CALLBACK FUNCTIONS</seealso>.
- A callback module provided by
- the user is registered using option <c>callback</c>, see
- <seealso marker="#options">DATA TYPES</seealso>.</p>
+ <p>Interface module for the <c>tftp</c> application.</p>
</description>
-
- <section>
- <title>TFTP SERVER SERVICE START/STOP</title>
-
- <p>A TFTP server can be configured to start statically when starting
- the <c>Inets</c> application. Alternatively, it can be started dynamically
- (when <c>Inets</c> is already started) by calling the <c>Inets</c> application
- API <c>inets:start(tftpd, ServiceConfig)</c> or
- <c>inets:start(tftpd, ServiceConfig, How)</c>,
- see <seealso marker="inets">inets(3)</seealso> for details.
- The <c>ServiceConfig</c> for TFTP is described in
- the <seealso marker="#options">DATA TYPES</seealso>
- section.</p>
-
- <p>The TFTP server can be stopped using <c>inets:stop(tftpd, Pid)</c>,
- see <seealso marker="inets">inets(3)</seealso> for details.</p>
- <p>The TPFT client is of such a temporary nature that it is not
- handled as a service in the <c>Inets</c> service framework.</p>
-
- </section>
-
<section>
<marker id="options"></marker>
<title>DATA TYPES</title>
diff --git a/lib/tftp/doc/src/usersguide.xml b/lib/tftp/doc/src/usersguide.xml
new file mode 100644
index 0000000000..eb7f7d17c3
--- /dev/null
+++ b/lib/tftp/doc/src/usersguide.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2004</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>TFTP User's Guide</title>
+ <prepared>Péter Dimitrov</prepared>
+ <docno></docno>
+ <date>2018-03-22</date>
+ <rev>A</rev>
+ <file>part.sgml</file>
+ </header>
+ <description>
+ <p>The <c>TFTP</c> application provides a TFTP client and server.</p>
+ </description>
+ <xi:include href="introduction.xml"/>
+ <xi:include href="getting_started.xml"/>
+</part>
diff --git a/lib/tftp/ebin/.gitignore b/lib/tftp/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/tftp/ebin/.gitignore
diff --git a/lib/tftp/info b/lib/tftp/info
new file mode 100644
index 0000000000..1220454351
--- /dev/null
+++ b/lib/tftp/info
@@ -0,0 +1,2 @@
+group: comm
+short: TFTP application
diff --git a/lib/inets/src/tftp/Makefile b/lib/tftp/src/Makefile
index 4eaa959cce..ed1551ba04 100644
--- a/lib/inets/src/tftp/Makefile
+++ b/lib/tftp/src/Makefile
@@ -20,30 +20,27 @@
#
include $(ERL_TOP)/make/target.mk
-EBIN = ../../ebin
include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
-include ../../vsn.mk
-
-VSN = $(INETS_VSN)
-
+include ../vsn.mk
+VSN = $(TFTP_VSN)
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-BEHAVIOUR_MODULES= \
- tftp
+BEHAVIOUR_MODULES=
MODULES = \
+ tftp \
+ tftp_app \
tftp_binary \
tftp_engine \
tftp_file \
@@ -61,18 +58,18 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR))
+APP_FILE= tftp.app
+APPUP_FILE= tftp.appup
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-include ../inets_app/inets.mk
-
-ERL_COMPILE_FLAGS += \
- $(INETS_FLAGS) \
- $(INETS_ERL_COMPILE_FLAGS) \
- -I../../include \
- -I../inets_app
-
# ----------------------------------------------------
# Targets
@@ -80,12 +77,18 @@ ERL_COMPILE_FLAGS += \
$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
-debug opt: $(TARGET_FILES)
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
rm -f core
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
+
docs:
# ----------------------------------------------------
@@ -94,16 +97,14 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/src"
- $(INSTALL_DIR) "$(RELSYSDIR)/src/tftp"
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/tftp"
- $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
- $(INSTALL_DATA) $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DIR) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \
+ $(APPUP_TARGET) "$(RELSYSDIR)/ebin"
release_docs_spec:
info:
@echo "APPLICATION = $(APPLICATION)"
- @echo "INETS_DEBUG = $(INETS_DEBUG)"
- @echo "INETS_FLAGS = $(INETS_FLAGS)"
@echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)"
diff --git a/lib/tftp/src/tftp.app.src b/lib/tftp/src/tftp.app.src
new file mode 100644
index 0000000000..2a87d39ada
--- /dev/null
+++ b/lib/tftp/src/tftp.app.src
@@ -0,0 +1,22 @@
+{application, tftp,
+ [{description, "TFTP application"},
+ {vsn, "1.0"},
+ {registered, []},
+ {mod, { tftp_app, []}},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, [
+ tftp,
+ tftp_app,
+ tftp_binary,
+ tftp_engine,
+ tftp_file,
+ tftp_lib,
+ tftp_logger,
+ tftp_sup
+ ]},
+ {runtime_dependencies, ["stdlib-3.5","kernel-6.0"]}
+ ]}.
diff --git a/lib/tftp/src/tftp.appup.src b/lib/tftp/src/tftp.appup.src
new file mode 100644
index 0000000000..06a0f0f9dc
--- /dev/null
+++ b/lib/tftp/src/tftp.appup.src
@@ -0,0 +1,26 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+{"%VSN%",
+ [
+ {<<".*">>,[{restart_application, tftp}]}
+ ],
+ [
+ {<<".*">>,[{restart_application, tftp}]}
+ ]
+}.
diff --git a/lib/inets/src/tftp/tftp.erl b/lib/tftp/src/tftp.erl
index c8804ea55f..27ed13694b 100644
--- a/lib/inets/src/tftp/tftp.erl
+++ b/lib/tftp/src/tftp.erl
@@ -213,7 +213,8 @@
start/1,
info/1,
change_config/2,
- start/0
+ start/0,
+ stop/0
]).
%% Application local functions
@@ -373,7 +374,17 @@ change_config(Pid, Options) ->
%%-------------------------------------------------------------------
start() ->
- application:start(inets).
+ application:start(tftp).
+
+%%-------------------------------------------------------------------
+%% stop() -> ok | {error, Reason}
+%%
+%% Reason = term()
+%%
+%% Stop the application
+%%-------------------------------------------------------------------
+stop() ->
+ application:stop(tftp).
%%-------------------------------------------------------------------
%% Inets service behavior
diff --git a/lib/inets/src/tftp/tftp.hrl b/lib/tftp/src/tftp.hrl
index 25543e0b9e..25543e0b9e 100644
--- a/lib/inets/src/tftp/tftp.hrl
+++ b/lib/tftp/src/tftp.hrl
diff --git a/lib/tftp/src/tftp_app.erl b/lib/tftp/src/tftp_app.erl
new file mode 100644
index 0000000000..80d54c6cbe
--- /dev/null
+++ b/lib/tftp/src/tftp_app.erl
@@ -0,0 +1,56 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%%-------------------------------------------------------------------
+%% @doc ftp public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(tftp_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ Config = get_configuration(),
+ tftp_sup:start_link(Config).
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+get_configuration() ->
+ case (catch application:get_env(tftp, services)) of
+ {ok, Services} ->
+ Services;
+ _ ->
+ []
+ end.
diff --git a/lib/inets/src/tftp/tftp_binary.erl b/lib/tftp/src/tftp_binary.erl
index 09adcfc41f..09adcfc41f 100644
--- a/lib/inets/src/tftp/tftp_binary.erl
+++ b/lib/tftp/src/tftp_binary.erl
diff --git a/lib/inets/src/tftp/tftp_engine.erl b/lib/tftp/src/tftp_engine.erl
index fb2c9749e5..f14354ad6a 100644
--- a/lib/inets/src/tftp/tftp_engine.erl
+++ b/lib/tftp/src/tftp_engine.erl
@@ -203,19 +203,19 @@ daemon_loop(#daemon_state{config = DaemonConfig,
Fun = fun(#server_info{pid = Pid}, Acc) -> [{server, Pid} | Acc] end,
ServerInfo = ets:foldl(Fun, [], ServerTab),
Info = internal_info(DaemonConfig, daemon) ++ [{n_conn, N}] ++ ServerInfo,
- reply({ok, Info}, Ref, FromPid),
+ _ = reply({ok, Info}, Ref, FromPid),
?MODULE:daemon_loop(State);
{{change_config, Options}, Ref, FromPid} when is_pid(FromPid) ->
case catch tftp_lib:parse_config(Options, DaemonConfig) of
{'EXIT', Reason} ->
- reply({error, Reason}, Ref, FromPid),
+ _ = reply({error, Reason}, Ref, FromPid),
?MODULE:daemon_loop(State);
DaemonConfig2 when is_record(DaemonConfig2, config) ->
- reply(ok, Ref, FromPid),
+ _ = reply(ok, Ref, FromPid),
?MODULE:daemon_loop(State#daemon_state{config = DaemonConfig2})
end;
{udp, Socket, RemoteHost, RemotePort, Bin} when is_binary(Bin) ->
- inet:setopts(Socket, [{active, once}]),
+ _ = inet:setopts(Socket, [{active, once}]),
ServerConfig = DaemonConfig#config{parent_pid = self(),
udp_host = RemoteHost,
udp_port = RemotePort},
@@ -449,14 +449,14 @@ client_prepare(Config, Callback, Req) when is_record(Req, tftp_msg_req) ->
transfer(Config2, Callback2, Req2, Req2, LocalAccess, BlockNo, #prepared{}),
client_open(Config3, Callback3, Req2, BlockNo, TransferRes);
{error, {Code, Text}} ->
- callback({abort, {Code, Text}}, Config, Callback2, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback2, Req),
terminate(Config, Req, ?ERROR(post_verify_options, Code, Text, Req#tftp_msg_req.filename))
end;
{undefined, #tftp_msg_error{code = Code, text = Text}} ->
terminate(Config, Req, ?ERROR(client_prepare, Code, Text, Req#tftp_msg_req.filename))
end;
{error, {Code, Text}} ->
- callback({abort, {Code, Text}}, Config, Callback, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback, Req),
terminate(Config, Req, ?ERROR(pre_verify_options, Code, Text, Req#tftp_msg_req.filename))
end.
@@ -500,10 +500,10 @@ client_open(Config, Callback, Req, BlockNo, #transfer_res{status = Status, decod
%% Req2 = Req#tftp_msg_req{options = []},
%% client_prepare(Config, Callback, Req2);
#tftp_msg_error{code = Code, text = Text} ->
- callback({abort, {Code, Text}}, Config, Callback, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback, Req),
terminate(Config, Req, ?ERROR(client_open, Code, Text, Req#tftp_msg_req.filename));
{'EXIT', #tftp_msg_error{code = Code, text = Text}} ->
- callback({abort, {Code, Text}}, Config, Callback, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback, Req),
terminate(Config, Req, ?ERROR(client_open, Code, Text, Req#tftp_msg_req.filename));
Msg when is_tuple(Msg) ->
Code = badop,
@@ -516,7 +516,7 @@ client_open(Config, Callback, Req, BlockNo, #transfer_res{status = Status, decod
end;
error when is_record(Prepared, tftp_msg_error) ->
#tftp_msg_error{code = Code, text = Text} = Prepared,
- callback({abort, {Code, Text}}, Config, Callback, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback, Req),
terminate(Config, Req, ?ERROR(client_open, Code, Text, Req#tftp_msg_req.filename))
end.
@@ -568,10 +568,10 @@ common_loop(Config, Callback, Req, #transfer_res{status = Status, decoded_msg =
#tftp_msg_data{block_no = ActualBlockNo, data = Data} when LocalAccess =:= write ->
common_write(Config, Callback, Req, LocalAccess, ExpectedBlockNo, ActualBlockNo, Data, Prepared);
#tftp_msg_error{code = Code, text = Text} ->
- callback({abort, {Code, Text}}, Config, Callback, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback, Req),
terminate(Config, Req, ?ERROR(common_loop, Code, Text, Req#tftp_msg_req.filename));
{'EXIT', #tftp_msg_error{code = Code, text = Text} = Error} ->
- callback({abort, {Code, Text}}, Config, Callback, Req),
+ _ = callback({abort, {Code, Text}}, Config, Callback, Req),
send_msg(Config, Req, Error),
terminate(Config, Req, ?ERROR(common_loop, Code, Text, Req#tftp_msg_req.filename));
Msg when is_tuple(Msg) ->
@@ -918,7 +918,7 @@ wait_for_msg(Config, Callback, Req) ->
{udp, Socket, RemoteHost, RemotePort, Bin}
when is_binary(Bin), Callback#callback.block_no =:= undefined ->
%% Client prepare
- inet:setopts(Socket, [{active, once}]),
+ _ = inet:setopts(Socket, [{active, once}]),
Config2 = Config#config{udp_host = RemoteHost,
udp_port = RemotePort},
DecodedMsg = (catch tftp_lib:decode_msg(Bin)),
@@ -927,7 +927,7 @@ wait_for_msg(Config, Callback, Req) ->
{udp, Socket, Host, Port, Bin} when is_binary(Bin),
Config#config.udp_host =:= Host,
Config#config.udp_port =:= Port ->
- inet:setopts(Socket, [{active, once}]),
+ _ = inet:setopts(Socket, [{active, once}]),
DecodedMsg = (catch tftp_lib:decode_msg(Bin)),
print_debug_info(Config, Req, recv, DecodedMsg),
{Config, DecodedMsg};
@@ -938,15 +938,15 @@ wait_for_msg(Config, Callback, Req) ->
false -> server
end,
Info = internal_info(Config, Type),
- reply({ok, Info}, Ref, FromPid),
+ _ = reply({ok, Info}, Ref, FromPid),
wait_for_msg(Config, Callback, Req);
{{change_config, Options}, Ref, FromPid} when is_pid(FromPid) ->
case catch tftp_lib:parse_config(Options, Config) of
{'EXIT', Reason} ->
- reply({error, Reason}, Ref, FromPid),
+ _ = reply({error, Reason}, Ref, FromPid),
wait_for_msg(Config, Callback, Req);
Config2 when is_record(Config2, config) ->
- reply(ok, Ref, FromPid),
+ _ = reply(ok, Ref, FromPid),
wait_for_msg(Config2, Callback, Req)
end;
{system, From, Msg} ->
@@ -1076,7 +1076,7 @@ do_callback({open, Type}, Config, Callback, Req)
Req#tftp_msg_req.options,
Callback#callback.state],
PeerInfo = peer_info(Config),
- fast_ensure_loaded(Mod),
+ _ = fast_ensure_loaded(Mod),
Args2 =
case erlang:function_exported(Mod, Fun, length(Args)) of
true -> Args;
@@ -1295,7 +1295,7 @@ info_msg(#config{logger = Logger}, F, A) ->
safe_apply(Logger, info_msg, [F, A]).
safe_apply(Mod, Fun, Args) ->
- fast_ensure_loaded(Mod),
+ _ = fast_ensure_loaded(Mod),
apply(Mod, Fun, Args).
fast_ensure_loaded(Mod) ->
diff --git a/lib/inets/src/tftp/tftp_file.erl b/lib/tftp/src/tftp_file.erl
index 7664324808..43b588f71a 100644
--- a/lib/inets/src/tftp/tftp_file.erl
+++ b/lib/tftp/src/tftp_file.erl
@@ -215,13 +215,13 @@ read(#state{access = read} = State) ->
Count = State#state.count + size(Bin),
{more, Bin, State#state{count = Count}};
{ok, Bin} when is_binary(Bin), size(Bin) < BlkSize ->
- file:close(State#state.fd),
+ _ = file:close(State#state.fd),
Count = State#state.count + size(Bin),
{last, Bin, Count};
eof ->
{last, <<>>, State#state.count};
{error, Reason} ->
- file:close(State#state.fd),
+ _ = file:close(State#state.fd),
{error, file_error(Reason)}
end;
read(State) ->
@@ -255,12 +255,12 @@ write(Bin, #state{access = write} = State) when is_binary(Bin) ->
Count = State#state.count + Size,
{more, State#state{count = Count}};
ok when Size < BlkSize->
- file:close(State#state.fd),
+ _ = file:close(State#state.fd),
Count = State#state.count + Size,
{last, Count};
{error, Reason} ->
- file:close(State#state.fd),
- file:delete(State#state.filename),
+ _ = file:close(State#state.fd),
+ _ = file:delete(State#state.filename),
{error, file_error(Reason)}
end;
write(Bin, State) ->
@@ -281,7 +281,7 @@ write(Bin, State) ->
%%-------------------------------------------------------------------
abort(_Code, _Text, #state{fd = Fd, access = Access} = State) ->
- file:close(Fd),
+ _ = file:close(Fd),
case Access of
write ->
ok = file:delete(State#state.filename);
diff --git a/lib/inets/src/tftp/tftp_lib.erl b/lib/tftp/src/tftp_lib.erl
index 454754f0a3..454754f0a3 100644
--- a/lib/inets/src/tftp/tftp_lib.erl
+++ b/lib/tftp/src/tftp_lib.erl
diff --git a/lib/inets/src/tftp/tftp_logger.erl b/lib/tftp/src/tftp_logger.erl
index a869958484..a869958484 100644
--- a/lib/inets/src/tftp/tftp_logger.erl
+++ b/lib/tftp/src/tftp_logger.erl
diff --git a/lib/inets/src/tftp/tftp_sup.erl b/lib/tftp/src/tftp_sup.erl
index 40b67c499c..0475e53e42 100644
--- a/lib/inets/src/tftp/tftp_sup.erl
+++ b/lib/tftp/src/tftp_sup.erl
@@ -19,7 +19,7 @@
%%
%%
%%----------------------------------------------------------------------
-%% Purpose: The top supervisor for tftp hangs under inets_sup.
+%% Purpose: The top supervisor for tftp
%%----------------------------------------------------------------------
-module(tftp_sup).
diff --git a/lib/tftp/test/Makefile b/lib/tftp/test/Makefile
new file mode 100644
index 0000000000..99f36256b0
--- /dev/null
+++ b/lib/tftp/test/Makefile
@@ -0,0 +1,250 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2018. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+#
+# For an outline of how this all_SUITE_data stuff works, see the
+# make file ../../ssl/test/Makefile.
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN = $(TFTP_VSN)
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+INCLUDES = -I. \
+ -I$(ERL_TOP)/lib/tftp/src
+
+CP = cp
+
+ifeq ($(TESTROOT_DIR),)
+TESTROOT_DIR = /ldisk/tests/$(USER)/tftp
+endif
+
+ifeq ($(TFTP_DATA_DIR),)
+TFTP_DATA_DIR = $(TESTROOT_DIR)/data_dir
+endif
+
+ifeq ($(TFTP_PRIV_DIR),)
+TFTP_PRIV_DIR = $(TESTROOT_DIR)/priv_dir
+endif
+
+TFTP_FLAGS = -Dtftp__data_dir='"$(TFTP_DATA_DIR)"' \
+ -Dtftp_priv_dir='"$(TFTP_PRIV_DIR)"'
+
+
+###
+### test suite debug flags
+###
+ifeq ($(TFTP_DEBUG_CLIENT),)
+ TFTP_DEBUG_CLIENT = y
+endif
+
+ifeq ($(TFTP_DEBUG_CLIENT),)
+ TFTP_FLAGS += -Dtftp_debug_client
+endif
+
+ifeq ($(TFTP_TRACE_CLIENT),)
+ TFTP_DEBUG_CLIENT = y
+endif
+
+ifeq ($(TFTP_TRACE_CLIENT),y)
+ TFTP_FLAGS += -Dtftp_trace_client
+endif
+
+ifneq ($(TFTP_DEBUG),)
+ TFTP_DEBUG = s
+endif
+
+ifeq ($(TFTP_DEBUG),l)
+ TFTP_FLAGS += -Dtftp_log
+endif
+
+ifeq ($(TFTP_DEBUG),d)
+ TFTP_FLAGS += -Dtftp_debug -Dtftp_log
+endif
+
+
+TFTP_FLAGS += -pa ../tftp/ebin
+
+TFTP_ROOT = ../tftp
+
+MODULES = \
+ tftp_SUITE \
+ tftp_test_lib
+
+
+EBIN = .
+
+HRL_FILES = \
+ ../src/tftp.hrl \
+ tftp_test_lib.hrl
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+SOURCE = $(ERL_FILES) $(HRL_FILES)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+TFTP_SPECS = tftp.spec tftp_bench.spec
+COVER_FILE = tftp.cover
+TFTP_FILES = tftp.config $(TFTP_SPECS)
+
+
+TFTP_DATADIRS = tftp_SUITE_data
+
+DATADIRS = $(TFTP_DATADIRS)
+
+EMAKEFILE = Emakefile
+MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
+
+ifeq ($(MAKE_EMAKE),)
+BUILDTARGET = $(TARGET_FILES)
+RELTEST_FILES = $(COVER_FILE) $(TFTP_SPECS) $(SOURCE)
+else
+BUILDTARGET = emakebuild
+RELTEST_FILES = $(EMAKEFILE) $(COVER_FILE) $(TFTP_SPECS) $(SOURCE)
+endif
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+
+RELTESTSYSDIR = "$(RELEASE_PATH)/tftp_test"
+RELTESTSYSALLDATADIR = $(RELTESTSYSDIR)/all_SUITE_data
+RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
+
+
+# ----------------------------------------------------
+# FLAGS
+# The path to the test_server ebin dir is needed when
+# running the target "targets".
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += \
+ $(INCLUDES) \
+ $(TFTP_FLAGS)
+
+# ----------------------------------------------------
+# Targets
+# erl -sname kalle -pa ../ebin
+# If you intend to run the test suite locally (private), then
+# there is some requirements:
+# 1) TFTP_PRIV_DIR must be created
+# ----------------------------------------------------
+
+tests debug opt: $(BUILDTARGET)
+
+targets: $(TARGET_FILES)
+
+.PHONY: emakebuild
+
+emakebuild: $(EMAKEFILE)
+
+$(EMAKEFILE):
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE)
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE)
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) "$(RELSYSDIR)/test"
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/test"
+ $(INSTALL_DATA) $(TFTP_FILES) "$(RELSYSDIR)/test"
+ @for d in $(DATADIRS); do \
+ echo "installing data dir $$d"; \
+ if test -f $$d/TAR.exclude; then \
+ echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
+ cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
+ find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
+ find $$d -name '*~' >> $$d/TAR.exclude2; \
+ find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
+ find $$d -name 'core' >> $$d/TAR.exclude2; \
+ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
+ tar cfX - $$d/TAR.exclude2 $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \
+ else \
+ tar cf - $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \
+ fi; \
+ done
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELTESTSYSDIR)
+ $(INSTALL_DATA) $(RELTEST_FILES) $(RELTESTSYSDIR)
+ chmod -R u+w $(RELTESTSYSDIR)
+ tar chf - $(DATADIRS) | (cd $(RELTESTSYSDIR); tar xf -)
+ $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)
+ $(INSTALL_DIR) $(RELTESTSYSBINDIR)
+ chmod -R +x $(RELTESTSYSBINDIR)
+ $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)/win32/lib
+
+release_docs_spec:
+
+info:
+ @echo "MAKE_EMAKE = $(MAKE_EMAKE)"
+ @echo "EMAKEFILE = $(EMAKEFILE)"
+ @echo "BUILDTARGET = $(BUILDTARGET)"
+ @echo ""
+ @echo "MODULES = $(MODULES)"
+ @echo "ERL_FILES = $(ERL_FILES)"
+ @echo "SOURCE = $(SOURCE)"
+ @echo "TARGET_FILES = $(TARGET_FILES)"
+ @echo ""
+ @echo "TFTP_SPECS = $(TFTP_SPECS)"
+ @echo "TFTP_FILES = $(TFTP_FILES)"
+ @echo ""
+ @echo "RELEASE_PATH = "$(RELEASE_PATH)""
+ @echo "RELSYSDIR = "$(RELSYSDIR)""
+ @echo "RELTESTSYSDIR = $(RELTESTSYSDIR)"
+ @echo "RELTESTSYSALLDATADIR = $(RELTESTSYSALLDATADIR)"
+ @echo "RELTESTSYSBINDIR = $(RELTESTSYSBINDIR)"
+ @echo ""
+ @echo "DATADIRS = $(DATADIRS)"
+ @echo "REL_DATADIRS = $(REL_DATADIRS)"
+ @echo ""
+ @echo "TFTP_DATA_DIR = $(TFTP_DATA_DIR)"
+ @echo "TFTP_PRIV_DIR = $(TFTP_PRIV_DIR)"
+ @echo "TFTP_ROOT = $(TFTP_ROOT)"
+ @echo "TFTP_FLAGS = $(TFTP_FLAGS)"
+
+
diff --git a/lib/tftp/test/tftp.config b/lib/tftp/test/tftp.config
new file mode 100644
index 0000000000..2600237da9
--- /dev/null
+++ b/lib/tftp/test/tftp.config
@@ -0,0 +1 @@
+[]. \ No newline at end of file
diff --git a/lib/tftp/test/tftp.cover b/lib/tftp/test/tftp.cover
new file mode 100644
index 0000000000..22ef5d0dda
--- /dev/null
+++ b/lib/tftp/test/tftp.cover
@@ -0,0 +1,2 @@
+{incl_app,tftp,details}.
+
diff --git a/lib/tftp/test/tftp.spec b/lib/tftp/test/tftp.spec
new file mode 100644
index 0000000000..f3537bc652
--- /dev/null
+++ b/lib/tftp/test/tftp.spec
@@ -0,0 +1 @@
+{suites,"../tftp_test", all}.
diff --git a/lib/inets/test/tftp_SUITE.erl b/lib/tftp/test/tftp_SUITE.erl
index 09049e36af..fd1d209c25 100644
--- a/lib/inets/test/tftp_SUITE.erl
+++ b/lib/tftp/test/tftp_SUITE.erl
@@ -26,21 +26,22 @@
%% Includes and defines
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-include_lib("common_test/include/ct.hrl").
-include("tftp_test_lib.hrl").
--define(START_DAEMON(PortX, OptionsX),
- fun(Port, Options) ->
- {ok, Pid} = ?VERIFY({ok, _Pid}, tftp:start([{port, Port} | Options])),
- if
- Port == 0 ->
- {ok, ActualOptions} = ?IGNORE(tftp:info(Pid)),
- {value, {port, ActualPort}} =
- lists:keysearch(port, 1, ActualOptions),
- {ActualPort, Pid};
- true ->
- {Port, Pid}
- end
- end(PortX, OptionsX)).
+-define(START_DAEMON(Port, Options),
+ begin
+ {ok, Pid} = ?VERIFY({ok, _Pid}, tftp:start([{port, Port} | Options])),
+ if
+ Port == 0 ->
+ {ok, ActualOptions} = ?IGNORE(tftp:info(Pid)),
+ {value, {port, ActualPort}} =
+ lists:keysearch(port, 1, ActualOptions),
+ {ActualPort, Pid};
+ true ->
+ {Port, Pid}
+ end
+ end).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% API
@@ -74,9 +75,18 @@ end_per_testcase(Case, Config) when is_list(Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [simple, extra, reuse_connection, resend_client,
- resend_server, large_file].
+all() ->
+ [
+ simple,
+ extra,
+ reuse_connection,
+ resend_client,
+ resend_server,
+ large_file,
+ app,
+ appup,
+ start_tftpd
+ ].
groups() ->
[].
@@ -94,6 +104,48 @@ end_per_group(_GroupName, Config) ->
Config.
+app() ->
+ [{doc, "Test that the tftp app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(tftp).
+
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the tftp appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(tftp).
+
+start_tftpd() ->
+ [{doc, "Start/stop of tfpd service"}].
+start_tftpd(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ok = tftp:start(),
+ {ok, Pid0} = tftp:start_service([{host, "localhost"}, {port, 0}]),
+ Pids0 = [ServicePid || {_, ServicePid} <- tftp:services()],
+ true = lists:member(Pid0, Pids0),
+ {ok, [_|_]} = tftp:service_info(Pid0),
+ tftp:stop_service(Pid0),
+ ct:sleep(100),
+ Pids1 = [ServicePid || {_, ServicePid} <- tftp:services()],
+ false = lists:member(Pid0, Pids1),
+
+ {ok, Pid1} =
+ tftp:start_standalone([{host, "localhost"}, {port, 0}]),
+ Pids2 = [ServicePid || {_, ServicePid} <- tftp:services()],
+ false = lists:member(Pid1, Pids2),
+ %% Standalone service is not supervised
+ {error,not_found} = tftp:stop_service(Pid1),
+ ok = tftp:stop(),
+
+ application:load(tftp),
+ application:set_env(tftp, services, [{tftpd, [{host, "localhost"},
+ {port, 0}]}]),
+ ok = tftp:start(),
+ 1 = length(tftp:services()),
+ application:unset_env(tftp, services),
+ ok = tftp:stop().
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Simple
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -103,7 +155,7 @@ simple(doc) ->
simple(suite) ->
[];
simple(Config) when is_list(Config) ->
- ?VERIFY(ok, application:start(inets)),
+ ?VERIFY(ok, application:start(tftp)),
{Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, brief}])),
@@ -128,7 +180,7 @@ simple(Config) when is_list(Config) ->
exit(DaemonPid, kill),
?VERIFY(ok, file:delete(LocalFilename)),
?VERIFY(ok, file:delete(RemoteFilename)),
- ?VERIFY(ok, application:stop(inets)),
+ ?VERIFY(ok, application:stop(tftp)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -910,7 +962,7 @@ large_file(doc) ->
large_file(suite) ->
[];
large_file(Config) when is_list(Config) ->
- ?VERIFY(ok, application:start(inets)),
+ ?VERIFY(ok, application:start(tftp)),
{Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, brief}])),
@@ -933,7 +985,7 @@ large_file(Config) when is_list(Config) ->
exit(DaemonPid, kill),
?VERIFY(ok, file:delete(LocalFilename)),
?VERIFY(ok, file:delete(RemoteFilename)),
- ?VERIFY(ok, application:stop(inets)),
+ ?VERIFY(ok, application:stop(tftp)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/tftp/test/tftp_bench.spec b/lib/tftp/test/tftp_bench.spec
new file mode 100644
index 0000000000..43fa385c85
--- /dev/null
+++ b/lib/tftp/test/tftp_bench.spec
@@ -0,0 +1 @@
+{suites,"../tftp_test",[]}.
diff --git a/lib/inets/test/tftp_test_lib.erl b/lib/tftp/test/tftp_test_lib.erl
index f07795324f..45386389cb 100644
--- a/lib/inets/test/tftp_test_lib.erl
+++ b/lib/tftp/test/tftp_test_lib.erl
@@ -30,11 +30,11 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
io:format("\n ", []),
- ?IGNORE(application:stop(inets)),
+ ?IGNORE(application:stop(tftp)),
Config.
end_per_testcase(_Case, Config) when is_list(Config) ->
- ?IGNORE(application:stop(inets)),
+ ?IGNORE(application:stop(tftp)),
Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/inets/test/tftp_test_lib.hrl b/lib/tftp/test/tftp_test_lib.hrl
index e7a5a37d2c..e7a5a37d2c 100644
--- a/lib/inets/test/tftp_test_lib.hrl
+++ b/lib/tftp/test/tftp_test_lib.hrl
diff --git a/lib/tftp/vsn.mk b/lib/tftp/vsn.mk
new file mode 100644
index 0000000000..f1b0851a8f
--- /dev/null
+++ b/lib/tftp/vsn.mk
@@ -0,0 +1,24 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2018. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+
+APPLICATION = tftp
+TFTP_VSN = 1.0
+PRE_VSN =
+APP_VSN = "$(APPLICATION)-$(TFTP_VSN)$(PRE_VSN)"
diff --git a/scripts/run-dialyzer b/scripts/run-dialyzer
index 05c1fd63c0..c9da647952 100755
--- a/scripts/run-dialyzer
+++ b/scripts/run-dialyzer
@@ -2,9 +2,9 @@
set -e
-$ERL_TOP/bin/dialyzer --build_plt --apps asn1 compiler crypto dialyzer edoc erts et hipe inets kernel mnesia observer public_key runtime_tools snmp ssh ssl stdlib syntax_tools wx xmerl --statistics
-$ERL_TOP/bin/dialyzer -n -Wunknown -Wunmatched_returns --apps compiler erts kernel stdlib asn1 crypto dialyzer hipe parsetools public_key runtime_tools sasl tools --statistics
-$ERL_TOP/bin/dialyzer -n --apps common_test debugger edoc inets mnesia observer ssh ssl syntax_tools wx xmerl --statistics
+$ERL_TOP/bin/dialyzer --build_plt --apps asn1 compiler crypto dialyzer edoc erts et ftp hipe inets kernel mnesia observer public_key runtime_tools snmp ssh ssl stdlib syntax_tools tftp wx xmerl --statistics
+$ERL_TOP/bin/dialyzer -n -Wunknown -Wunmatched_returns --apps compiler erts ftp tftp kernel stdlib asn1 crypto dialyzer hipe parsetools public_key runtime_tools sasl tools --statistics
+$ERL_TOP/bin/dialyzer -n --apps common_test debugger edoc ftp inets mnesia observer ssh ssl syntax_tools tftp wx xmerl --statistics
# In travis we don't dialyze everything as it takes too much time
if [ "X$DIALYZE_ALL_APPLICATIONS" = "Xtrue" ]; then