diff options
Diffstat (limited to 'lib')
34 files changed, 273 insertions, 155 deletions
diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml index eba3c3030d..8e85cccc99 100644 --- a/lib/common_test/doc/src/ct_telnet.xml +++ b/lib/common_test/doc/src/ct_telnet.xml @@ -337,7 +337,7 @@ <c>FullMatch</c> is the string matched by the whole regular expression, and <c>SubMatchN</c> is the string that matched subexpression number <c>N</c>. Subexpressions are denoted with - <c>(' ')</c> in the regular expression.</p> + <c>'(' ')'</c> in the regular expression.</p> <p>If a <c>Tag</c> is speciifed, the returned <c>Match</c> also includes the matched <c>Tag</c>. Otherwise, only <c>RxMatch</c> diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 63597a5ede..0e4e85cef7 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -51,9 +51,11 @@ #include <openssl/bn.h> #include <openssl/objects.h> #ifndef OPENSSL_NO_RC4 -#include <openssl/rc4.h> + #include <openssl/rc4.h> #endif /* OPENSSL_NO_RC4 */ -#include <openssl/rc2.h> +#ifndef OPENSSL_NO_RC2 + #include <openssl/rc2.h> +#endif #include <openssl/blowfish.h> #include <openssl/rand.h> #include <openssl/evp.h> @@ -470,7 +472,13 @@ struct cipher_type_t { struct cipher_type_t cipher_types[] = { - {{"rc2_cbc"}, {&EVP_rc2_cbc}}, + {{"rc2_cbc"}, +#ifndef OPENSSL_NO_RC2 + {&EVP_rc2_cbc} +#else + {NULL} +#endif + }, {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}}, {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}}, {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}}, @@ -829,7 +837,9 @@ static void init_algorithms_types(ErlNifEnv* env) algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb"); +#ifndef OPENSSL_NO_RC2 algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc"); +#endif #ifndef OPENSSL_NO_RC4 algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4"); #endif diff --git a/lib/crypto/test/old_crypto_SUITE.erl b/lib/crypto/test/old_crypto_SUITE.erl index 579b14b0eb..324ed39c6d 100644 --- a/lib/crypto/test/old_crypto_SUITE.erl +++ b/lib/crypto/test/old_crypto_SUITE.erl @@ -1080,7 +1080,9 @@ rc2_cbc(doc) -> "Encrypt and decrypt according to RC2 CBC and check the result. " "Example stripped out from public_key application test"; rc2_cbc(Config) when is_list(Config) -> - + if_supported(rc2_cbc, fun rc2_cbc_do/0). + +rc2_cbc_do() -> Key = <<146,210,160,124,215,227,153,239,227,17,222,140,3,93,27,191>>, IV = <<72,91,135,182,25,42,35,210>>, diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index 91256fa6a2..d8bdac24e3 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -110,27 +110,30 @@ result(Response = {{_, 300, _}, _, _}, redirect(Response, Request); result(Response = {{_, Code, _}, _, _}, + Request = #request{settings = + #http_options{autoredirect = true}, + method = post}) when (Code =:= 301) orelse + (Code =:= 302) orelse + (Code =:= 303) -> + redirect(Response, Request#request{method = get}); +result(Response = {{_, Code, _}, _, _}, + Request = #request{settings = + #http_options{autoredirect = true}, + method = post}) when (Code =:= 307) -> + redirect(Response, Request); +result(Response = {{_, Code, _}, _, _}, Request = #request{settings = #http_options{autoredirect = true}, - method = head}) when (Code =:= 301) orelse + method = Method}) when (Code =:= 301) orelse (Code =:= 302) orelse (Code =:= 303) orelse (Code =:= 307) -> - redirect(Response, Request); -result(Response = {{_, Code, _}, _, _}, - Request = #request{settings = - #http_options{autoredirect = true}, - method = get}) when (Code =:= 301) orelse - (Code =:= 302) orelse - (Code =:= 303) orelse - (Code =:= 307) -> - redirect(Response, Request); -result(Response = {{_, 303, _}, _, _}, - Request = #request{settings = - #http_options{autoredirect = true}, - method = post}) -> - redirect(Response, Request#request{method = get}); - + case lists:member(Method, [get, head, options, trace]) of + true -> + redirect(Response, Request); + false -> + transparent(Response, Request) + end; result(Response = {{_,503,_}, _, _}, Request) -> status_service_unavailable(Response, Request); diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 932567ec55..57da82c6ad 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -500,10 +500,11 @@ redirect_multiple_choises(Config) when is_list(Config) -> httpc:request(get, {URL300, []}, [{autoredirect, false}], []). %%------------------------------------------------------------------------- redirect_moved_permanently() -> - [{doc, "If the 301 status code is received in response to a request other " - "than GET or HEAD, the user agent MUST NOT automatically redirect the request " - "unless it can be confirmed by the user, since this might change " - "the conditions under which the request was issued."}]. + [{doc, "The server SHOULD generate a Location header field in the response " + "containing a preferred URI reference for the new permanent URI. The user " + "agent MAY use the Location field value for automatic redirection. The server's " + "response payload usually contains a short hypertext note with a " + "hyperlink to the new URI(s)."}]. redirect_moved_permanently(Config) when is_list(Config) -> URL301 = url(group_name(Config), "/301.html", Config), @@ -514,15 +515,16 @@ redirect_moved_permanently(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], []}} = httpc:request(head, {URL301, []}, [], []), - {ok, {{_,301,_}, [_ | _], [_|_]}} + {ok, {{_,200,_}, [_ | _], [_|_]}} = httpc:request(post, {URL301, [],"text/plain", "foobar"}, [], []). %%------------------------------------------------------------------------- redirect_found() -> - [{doc," If the 302 status code is received in response to a request other " - "than GET or HEAD, the user agent MUST NOT automatically redirect the " - "request unless it can be confirmed by the user, since this might change " - "the conditions under which the request was issued."}]. + [{doc, "The server SHOULD generate a Location header field in the response " + "containing a URI reference for the different URI. The user agent MAY " + "use the Location field value for automatic redirection. The server's " + "response payload usually contains a short hypertext note with a " + "hyperlink to the different URI(s)."}]. redirect_found(Config) when is_list(Config) -> URL302 = url(group_name(Config), "/302.html", Config), @@ -533,14 +535,14 @@ redirect_found(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], []}} = httpc:request(head, {URL302, []}, [], []), - {ok, {{_,302,_}, [_ | _], [_|_]}} + {ok, {{_,200,_}, [_ | _], [_|_]}} = httpc:request(post, {URL302, [],"text/plain", "foobar"}, [], []). %%------------------------------------------------------------------------- redirect_see_other() -> [{doc, "The different URI SHOULD be given by the Location field in the response. " "Unless the request method was HEAD, the entity of the response SHOULD contain a short " - "hypertext note with a hyperlink to the new URI(s). "}]. + "hypertext note with a hyperlink to the new URI(s)."}]. redirect_see_other(Config) when is_list(Config) -> URL303 = url(group_name(Config), "/303.html", Config), @@ -556,10 +558,11 @@ redirect_see_other(Config) when is_list(Config) -> [], []). %%------------------------------------------------------------------------- redirect_temporary_redirect() -> - [{doc," If the 307 status code is received in response to a request other " - "than GET or HEAD, the user agent MUST NOT automatically redirect the request " - "unless it can be confirmed by the user, since this might change " - "the conditions under which the request was issued."}]. + [{doc, "The server SHOULD generate a Location header field in the response " + "containing a URI reference for the different URI. The user agent MAY " + "use the Location field value for automatic redirection. The server's " + "response payload usually contains a short hypertext note with a " + "hyperlink to the different URI(s)."}]. redirect_temporary_redirect(Config) when is_list(Config) -> URL307 = url(group_name(Config), "/307.html", Config), @@ -570,7 +573,7 @@ redirect_temporary_redirect(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], []}} = httpc:request(head, {URL307, []}, [], []), - {ok, {{_,307,_}, [_ | _], [_|_]}} + {ok, {{_,200,_}, [_ | _], [_|_]}} = httpc:request(post, {URL307, [],"text/plain", "foobar"}, [], []). diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 4db377bcde..3143cdc825 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -382,9 +382,14 @@ zip:create("mnesia-4.4.7.ez", <name name="add_pathsa" arity="1"/> <fsummary>Add directories to the beginning of the code path.</fsummary> <desc> - <p>Adds the directories in <c><anno>Dirs</anno></c> to the beginning of - the code path. If a <c><anno>Dir</anno></c> exists, it is removed - from the old position in the code path.</p> + <p>Traverses <c><anno>Dirs</anno></c> and adds + each <c><anno>Dir</anno></c> to the beginning of the code + path. This means that the order of <c><anno>Dirs</anno></c> + is reversed in the resulting code path. For example, if you + add <c>[Dir1,Dir2]</c>, the resulting path will + be <c>[Dir2,Dir1|OldCodePath]</c>.</p> + <p>If a <c><anno>Dir</anno></c> already exists in the code + path, it is removed from the old position.</p> <p>Always returns <c>ok</c>, regardless of the validity of each individual <c><anno>Dir</anno></c>.</p> </desc> diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index d16e200cb3..82cf73cbda 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,9 +18,9 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.* %% Down to - max one major revision back - [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.* }. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 5f049c6f99..c37d114a58 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -493,22 +493,13 @@ read_write_file(Config) when is_list(Config) -> %% Try writing and reading back some term SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, - ok = ?FILE_MODULE:write_file(Name,term_to_binary(SomeTerm)), - {ok,Bin1} = ?FILE_MODULE:read_file(Name), - SomeTerm = binary_to_term(Bin1), + Bin1 = term_to_binary(SomeTerm), + ok = do_read_write_file(Name, Bin1), %% Try a "null" term NullTerm = [], - ok = ?FILE_MODULE:write_file(Name,term_to_binary(NullTerm)), - {ok,Bin2} = ?FILE_MODULE:read_file(Name), - NullTerm = binary_to_term(Bin2), - - %% Try some "complicated" types - BigNum = 123456789012345678901234567890, - ComplTerm = {self(),make_ref(),BigNum,3.14159}, - ok = ?FILE_MODULE:write_file(Name,term_to_binary(ComplTerm)), - {ok,Bin3} = ?FILE_MODULE:read_file(Name), - ComplTerm = binary_to_term(Bin3), + Bin2 = term_to_binary(NullTerm), + ok = do_read_write_file(Name, Bin2), %% Try reading a nonexistent file Name2 = filename:join(RootDir, @@ -519,25 +510,42 @@ read_write_file(Config) when is_list(Config) -> {error, enoent} = ?FILE_MODULE:read_file(''), %% Try writing to a bad filename - {error, enoent} = - ?FILE_MODULE:write_file("",term_to_binary(NullTerm)), + {error, enoent} = do_read_write_file("", Bin2), %% Try writing something else than a binary - {error, badarg} = ?FILE_MODULE:write_file(Name,{1,2,3}), - {error, badarg} = ?FILE_MODULE:write_file(Name,self()), + {error, badarg} = do_read_write_file(Name, {1,2,3}), + {error, badarg} = do_read_write_file(Name, self()), %% Some non-term binaries - ok = ?FILE_MODULE:write_file(Name,[]), - {ok,Bin4} = ?FILE_MODULE:read_file(Name), - 0 = byte_size(Bin4), + ok = do_read_write_file(Name, []), - ok = ?FILE_MODULE:write_file(Name,[Bin1,[],[[Bin2]]]), - {ok,Bin5} = ?FILE_MODULE:read_file(Name), - {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)), + %% Write some iolists + ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]), + ok = do_read_write_file(Name, ["string",<<"binary">>]), + ok = do_read_write_file(Name, "pure string"), [] = flush(), ok. +do_read_write_file(Name, Data) -> + case ?FILE_MODULE:write_file(Name, Data) of + ok -> + BinData = iolist_to_binary(Data), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok = ?FILE_MODULE:write_file(Name, Data, []), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok = ?FILE_MODULE:write_file(Name, Data, [raw]), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok; + {error,_}=Res -> + Res = ?FILE_MODULE:write_file(Name, Data, []), + Res = ?FILE_MODULE:write_file(Name, Data, [raw]), + Res + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index 42b0fa1d8a..7cd15e15d3 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -114,7 +114,8 @@ ttb:p(all, call).</input></pre> <v>Opt = {file,Client} | {handler, FormatHandler} | {process_info,PI} | shell | {shell, ShellSpec} | {timer, TimerSpec} | {overload_check, {MSec, Module, Function}} | - {flush, MSec} | resume | {resume, FetchTimeout}</v> + {flush, MSec} | resume | {resume, FetchTimeout} | + {queue_size, QueueSize}</v> <v>TimerSpec = MSec | {MSec, StopOpts}</v> <v>MSec = FetchTimeout = integer()</v> <v>Module = Function = atom() </v> @@ -126,6 +127,7 @@ ttb:p(all, call).</input></pre> <v>FormatHandler = See format/2</v> <v>PI = true | false </v> <v>ShellSpec = true | false | only</v> + <v>QueueSize = non_neg_integer()</v> </type> <desc> <p>Starts a file trace port on all specified nodes @@ -147,6 +149,18 @@ ttb:p(all, call).</input></pre> <c>Client</c> must be <c>{local, File}</c>. All trace information is then sent to the trace control node where it is written to file.</p></item> + <tag><c>queue_size</c></tag> + <item><p>When tracing to shell or <c>{local,File}</c>, an ip + trace driver is used internally. The ip trace driver has a + queue of maximum <c>QueueSize</c> messages waiting to be + delivered. If the driver cannot deliver messages as fast as + they are produced, the queue size might be exceeded and + messages are dropped. This parameter is optional, and is + only useful if many <c>{drop,N}</c> trace messages are + received by the trace handler. It has no meaning if shell + or <c>{local,File}</c> is not used. See + <seealso marker="runtime_tools:dbg#trace_port/2">dbg:trace_port/2</seealso> + for more information about the ip trace driver.</p></item> <tag><c>process_info</c></tag> <item><p>Indicates if process information is to be collected. If <c>PI = true</c> (which is diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index ac6c4572eb..87a50e046b 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -78,6 +78,11 @@ do_tracer(Nodes0,PI,Client,Traci) -> do_tracer(Clients,PI,Traci) -> Shell = proplists:get_value(shell, Traci, false), + IpPortSpec = + case proplists:get_value(queue_size, Traci) of + undefined -> 0; + QS -> {0,QS} + end, DefShell = fun(Trace) -> dbg:dhandler(Trace, standard_io) end, {ClientSucc,Succ} = lists:foldl( @@ -98,7 +103,7 @@ do_tracer(Clients,PI,Traci) -> [_,H] = string:tokens(atom_to_list(N),"@"), H end, - case catch dbg:tracer(N,port,dbg:trace_port(ip,0)) of + case catch dbg:tracer(N,port,dbg:trace_port(ip,IpPortSpec)) of {ok,N} -> {ok,Port} = dbg:trace_port_control(N,get_listen_port), {ok,T} = dbg:get_tracer(N), @@ -160,6 +165,8 @@ opt([{resume,MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]}); opt([{flush,MSec}|O],{PI,Client,Traci}) -> opt(O,{PI,Client,[{flush, MSec}|Traci]}); +opt([{queue_size,QueueSize}|O],{PI,Client,Traci}) -> + opt(O,{PI,Client,[{queue_size,QueueSize}|Traci]}); opt([],Opt) -> ensure_opt(Opt). diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl index 15d42a4d9c..602e47404d 100644 --- a/lib/parsetools/src/leex.erl +++ b/lib/parsetools/src/leex.erl @@ -1586,6 +1586,8 @@ out_dfa_graph(St, DFA, DF) -> case file:open(St#leex.gfile, [write]) of {ok,Gfile} -> try + %% Set the same encoding as infile: + set_encoding(St, Gfile), io:fwrite(Gfile, "digraph DFA {~n", []), out_dfa_states(Gfile, DFA, DF), out_dfa_edges(Gfile, DFA), @@ -1621,7 +1623,7 @@ out_dfa_edges(File, DFA) -> foreach(fun (T) -> Crs = orddict:fetch(T, Tdict), Edgelab = dfa_edgelabel(Crs), - io:fwrite(File, " ~b -> ~b [label=\"~s\"];~n", + io:fwrite(File, " ~b -> ~b [label=\"~ts\"];~n", [S,T,Edgelab]) end, sort(orddict:fetch_keys(Tdict))) end, DFA). diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl index 949ef3c36e..84f9c996ac 100644 --- a/lib/parsetools/test/leex_SUITE.erl +++ b/lib/parsetools/test/leex_SUITE.erl @@ -408,12 +408,12 @@ unicode(Config) when is_list(Config) -> Ts = [{unicode_1, <<"%% -*- coding: utf-8 -*-\n" "Definitions.\n" - "RTLarrow = (←)\n" + "RTLarrow = (â)\n" "Rules.\n" - "{RTLarrow} : {token,{'<-',TokenLine}}.\n" + "{RTLarrow} : {token,{\"â\",TokenLine}}.\n" "Erlang code.\n" "-export([t/0]).\n" - "t() -> {ok, [{'<-', 1}], 1} = string(\"←\"), ok.">>, + "t() -> {ok, [{\"â\", 1}], 1} = string(\"â\"), ok.">>, default, ok}], @@ -1137,7 +1137,7 @@ run_test(Config, Def, Pre) -> XrlFile = filename:join(DataDir, DefFile), ErlFile = filename:join(DataDir, Filename), Opts = [return, warn_unused_vars,{outdir,DataDir}], - ok = file:write_file(XrlFile, Def, [{encoding, unicode}]), + ok = file:write_file(XrlFile, Def), LOpts = [return, {report, false} | case Pre of default -> diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml index 1f87932b6c..923a9f1dfb 100644 --- a/lib/public_key/doc/src/public_key_app.xml +++ b/lib/public_key/doc/src/public_key_app.xml @@ -61,7 +61,7 @@ <section> <title>DEPENDENCIES</title> <p>The <c>public_key</c> application uses the - Crypto application to preform cryptographic operations and the + Crypto application to perform cryptographic operations and the ASN-1 application to handle PKIX-ASN-1 specifications, hence these applications must be loaded for the <c>public_key</c> application to work. In an embedded environment this means they must be started with @@ -72,7 +72,7 @@ <section> <title>ERROR LOGGER AND EVENT HANDLERS</title> <p> The <c>public_key</c> application is a library application - and does not use the error logger. The functions will either sucssed + and does not use the error logger. The functions will either succeed or fail with a runtime error. </p> </section> diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl index 004eaefc27..44caf479e5 100644 --- a/lib/public_key/test/pbe_SUITE.erl +++ b/lib/public_key/test/pbe_SUITE.erl @@ -219,7 +219,12 @@ pbes2() -> pbes2(Config) when is_list(Config) -> decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config), decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), - decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config). + case lists:member(rc2_cbc, proplists:get_value(ciphers, crypto:supports())) of + true -> + decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config); + false -> + ok + end. check_key_info(#'PrivateKeyInfo'{privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?rsaEncryption}, diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index db04bfdf7b..95f74d4607 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -954,7 +954,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ as the tuple <c>{drop, N}</c> where <c>N</c> is the number of consecutive messages dropped. In case of heavy tracing, drop's are likely to occur, and they surely occur if no client is reading the trace - messages.</p> + messages. The default value of <c>QueSize</c> is 200.</p> </desc> </func> <func> diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index c0d4665bda..f17aa528ed 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -427,7 +427,7 @@ trace_port(file, Filename) -> trace_port1(file, Filename, nowrap); trace_port(ip, Portno) when is_integer(Portno) -> - trace_port(ip,{Portno,50}); + trace_port(ip,{Portno,200}); trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) -> fun() -> diff --git a/lib/sasl/doc/src/script.xml b/lib/sasl/doc/src/script.xml index 8ed132354d..b40ff28179 100644 --- a/lib/sasl/doc/src/script.xml +++ b/lib/sasl/doc/src/script.xml @@ -88,7 +88,7 @@ follows:</p> <list type="bulleted"> <item><c>-pa Dir1 Dir2 ... DirN</c> adds the directories - <c>Dir1, Dir2, ..., DirN</c> to the front of the initial + <c>DirN, DirN-1, ..., Dir2, Dir1</c> to the front of the initial load path.</item> <item><c>-pz Dir1 Dir2 ... DirN</c> adds the directories <c>Dir1, Dir2, ..., DirN</c> to the end of the initial diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl index 74cd2e081a..8af0ecc5f9 100644 --- a/lib/ssh/src/ssh_cli.erl +++ b/lib/ssh/src/ssh_cli.erl @@ -208,8 +208,15 @@ handle_msg({Group, Req}, #state{group = Group, buf = Buf, pty = Pty, write_chars(ConnectionHandler, ChannelId, Chars), {ok, State#state{buf = NewBuf}}; -handle_msg({'EXIT', Group, _Reason}, #state{group = Group, - channel = ChannelId} = State) -> +handle_msg({'EXIT', Group, Reason}, #state{group = Group, + cm = ConnectionHandler, + channel = ChannelId} = State) -> + Status = case Reason of + normal -> 0; + _ -> -1 + end, + ssh_connection:exit_status(ConnectionHandler, ChannelId, Status), + ssh_connection:send_eof(ConnectionHandler, ChannelId), {stop, ChannelId, State}; handle_msg(_, State) -> diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index d52d453007..51e0d5196b 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -67,7 +67,8 @@ shell_unicode_string/1, ssh_info_print/1, key_callback/1, - key_callback_options/1 + key_callback_options/1, + shell_exit_status/1 ]). %%% Common test callbacks @@ -106,7 +107,8 @@ all() -> multi_daemon_opt_fd, packet_size_zero, ssh_info_print, - {group, login_bad_pwd_no_retry} + {group, login_bad_pwd_no_retry}, + shell_exit_status ]. groups() -> @@ -1167,6 +1169,33 @@ login_bad_pwd_no_retry(Config, AuthMethods) -> end end. + +%%---------------------------------------------------------------------------- +%%% Test that when shell REPL exit with reason normal client receives status 0 +shell_exit_status(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = proplists:get_value(data_dir, Config), + UserDir = proplists:get_value(priv_dir, Config), + + ShellFun = fun (_User) -> spawn(fun() -> ok end) end, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, [{"vego", "morot"}]}, + {shell, ShellFun}, + {failfun, fun ssh_test_lib:failfun/2}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user, "vego"}, + {password, "morot"}, + {user_interaction, false}]), + + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ok = ssh_connection:shell(ConnectionRef, ChannelId), + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId), + ssh:stop_daemon(Pid). + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 212b99c695..09e707ad07 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.3.2 +SSH_VSN = 4.3.3 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index d02e21a013..c7f50777a8 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -28,6 +28,31 @@ <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 8.0.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A timing related bug in event handling could cause + interoperability problems between an erlang TLS server + and some TLS clients, especially noticed with Firefox as + TLS client.</p> + <p> + Own Id: OTP-13917</p> + </item> + <item> + <p> + Correct ECC curve selection, the error could cause the + default to always be selected.</p> + <p> + Own Id: OTP-13918</p> + </item> + </list> + </section> + +</section> + <section><title>SSL 8.0.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 479f68f4bb..4f1f050e4b 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -232,9 +232,7 @@ error(_, _, _) -> #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- -hello(internal, #client_hello{client_version = ClientVersion, - extensions = #hello_extensions{ec_point_formats = EcPointFormats, - elliptic_curves = EllipticCurves}} = Hello, +hello(internal, #client_hello{client_version = ClientVersion} = Hello, State = #state{connection_states = ConnectionStates0, port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, @@ -260,7 +258,6 @@ hello(internal, #client_hello{client_version = ClientVersion, negotiated_version = Version, hashsign_algorithm = HashSign, session = Session, - client_ecc = {EllipticCurves, EcPointFormats}, negotiated_protocol = Protocol}, ?MODULE) end; hello(internal, #server_hello{} = Hello, diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 7ed109341d..02873ce522 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -333,21 +333,27 @@ anonymous_suites({3, N}) -> anonymous_suites(N) when N >= 3 -> [?TLS_DH_anon_WITH_AES_128_GCM_SHA256, - ?TLS_DH_anon_WITH_AES_256_GCM_SHA384 - ] ++ anonymous_suites(0); - -anonymous_suites(_) -> - [?TLS_DH_anon_WITH_RC4_128_MD5, - ?TLS_DH_anon_WITH_DES_CBC_SHA, - ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, - ?TLS_DH_anon_WITH_AES_128_CBC_SHA, - ?TLS_DH_anon_WITH_AES_256_CBC_SHA, + ?TLS_DH_anon_WITH_AES_256_GCM_SHA384, ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, ?TLS_DH_anon_WITH_AES_256_CBC_SHA256, - ?TLS_ECDH_anon_WITH_RC4_128_SHA, - ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, ?TLS_ECDH_anon_WITH_AES_128_CBC_SHA, - ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA]. + ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_DH_anon_WITH_RC4_128_MD5]; + +anonymous_suites(2) -> + [?TLS_ECDH_anon_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_DH_anon_WITH_DES_CBC_SHA, + ?TLS_DH_anon_WITH_RC4_128_MD5]; + +anonymous_suites(N) when N == 0; + N == 1 -> + [?TLS_DH_anon_WITH_RC4_128_MD5, + ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_DH_anon_WITH_DES_CBC_SHA + ]. %%-------------------------------------------------------------------- -spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()]. diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 304d1706f5..08fca76123 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1430,13 +1430,14 @@ key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = St key_exchange(#state{role = server, key_algorithm = Algo, hashsign_algorithm = HashSignAlgo, private_key = PrivateKey, + session = #session{ecc = ECCCurve}, connection_states = ConnectionStates0, negotiated_version = Version } = State0, Connection) when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa; Algo == ecdh_anon -> - ECDHKeys = public_key:generate_key(select_curve(State0)), + ECDHKeys = public_key:generate_key(ECCCurve), #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), #security_parameters{client_random = ClientRandom, @@ -1845,11 +1846,6 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0 {Record, State} = prepare_connection(State1, Connection), Connection:next_event(connection, Record, State). -select_curve(#state{client_ecc = {[Curve|_], _}}) -> - {namedCurve, Curve}; -select_curve(_) -> - {namedCurve, ?secp256r1}. - is_anonymous(Algo) when Algo == dh_anon; Algo == ecdh_anon; Algo == psk; diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index f1e612a41b..fca3e11894 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -48,6 +48,7 @@ socket_options :: #socket_options{}, connection_states :: ssl_record:connection_states() | secret_printout(), protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl + unprocessed_handshake_events = 0 :: integer(), tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout() | 'undefined', cert_db :: reference() | 'undefined', @@ -81,7 +82,6 @@ expecting_next_protocol_negotiation = false ::boolean(), expecting_finished = false ::boolean(), negotiated_protocol = undefined :: undefined | binary(), - client_ecc, % {Curves, PointFmt} tracker :: pid() | 'undefined', %% Tracker process for listen socket sni_hostname = undefined, downgrade, diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 9b9031473a..932bb139c1 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -237,9 +237,7 @@ error(_, _, _) -> #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- -hello(internal, #client_hello{client_version = ClientVersion, - extensions = #hello_extensions{ec_point_formats = EcPointFormats, - elliptic_curves = EllipticCurves}} = Hello, +hello(internal, #client_hello{client_version = ClientVersion} = Hello, #state{connection_states = ConnectionStates0, port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, @@ -265,7 +263,6 @@ hello(internal, #client_hello{client_version = ClientVersion, negotiated_version = Version, hashsign_algorithm = HashSign, session = Session, - client_ecc = {EllipticCurves, EcPointFormats}, negotiated_protocol = Protocol}) end; hello(internal, #server_hello{} = Hello, @@ -421,7 +418,7 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, connection -> ssl_connection:hibernate_after(StateName, State, Events); _ -> - {next_state, StateName, State, Events} + {next_state, StateName, State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events} end catch throw:#alert{} = Alert -> ssl_connection:handle_own_alert(Alert, Version, StateName, State0) @@ -537,7 +534,9 @@ next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buf #alert{} = Alert -> Alert end. - +next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 -> + {no_record, State#state{unprocessed_handshake_events = N-1}}; + next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]} = Buffers, @@ -712,3 +711,10 @@ gen_info(Event, StateName, #state{negotiated_version = Version} = State) -> Version, StateName, State) end. +unprocessed_events(Events) -> + %% The first handshake event will be processed immediately + %% as it is entered first in the event queue and + %% when it is processed there will be length(Events)-1 + %% handshake events left to process before we should + %% process more TLS-records received on the socket. + erlang:length(Events)-1. diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 57963fd44b..322f93b94c 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -2171,7 +2171,7 @@ anonymous_cipher_suites()-> [{doc,"Test the anonymous ciphersuites"}]. anonymous_cipher_suites(Config) when is_list(Config) -> Version = ssl_test_lib:protocol_version(Config), - Ciphers = ssl_test_lib:anonymous_suites(), + Ciphers = ssl_test_lib:anonymous_suites(Version), run_suites(Ciphers, Version, Config, anonymous). %%------------------------------------------------------------------- psk_cipher_suites() -> @@ -2272,8 +2272,8 @@ default_reject_anonymous(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - [Cipher | _] = ssl_test_lib:anonymous_suites(), + Version = tls_record:highest_protocol_version(tls_record:supported_protocol_versions()), + [CipherSuite | _] = ssl_test_lib:anonymous_suites(Version), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -2283,7 +2283,7 @@ default_reject_anonymous(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {options, - [{ciphers,[Cipher]} | + [{ciphers,[CipherSuite]} | ClientOpts]}]), ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}}, @@ -4437,7 +4437,7 @@ run_suites(Ciphers, Version, Config, Type) -> anonymous -> %% No certs in opts! {ssl_test_lib:ssl_options(client_verification_opts, Config), - ssl_test_lib:ssl_options(server_anon, Config)}; + [{reuseaddr, true}, {ciphers, ssl_test_lib:anonymous_suites(Version)}]}; psk -> {ssl_test_lib:ssl_options(client_psk, Config), ssl_test_lib:ssl_options(server_psk, Config)}; diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 81a49776e4..942e68967a 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -2011,26 +2011,19 @@ active_once_raw(Socket, Data, N) -> active_once_raw(_, _, 0, _) -> ok; -active_once_raw(Socket, Data, N, Acc) -> - receive - {ssl, Socket, Byte} when length(Byte) == 1 -> - ssl:setopts(Socket, [{active, once}]), +active_once_raw(Socket, Data, N, Acc0) -> + case lists:prefix(Data, Acc0) of + true -> + DLen = length(Data), + Start = DLen + 1, + Len = length(Acc0) - DLen, + Acc = string:substr(Acc0, Start, Len), + active_once_raw(Socket, Data, N-1, Acc); + false -> receive - {ssl, Socket, _} -> - ssl:setopts(Socket, [{active, once}]), - active_once_raw(Socket, Data, N-1, []) - end; - {ssl, Socket, Data} -> - ssl:setopts(Socket, [{active, once}]), - active_once_raw(Socket, Data, N-1, []); - {ssl, Socket, Other} -> - case Acc ++ Other of - Data -> - ssl:setopts(Socket, [{active, once}]), - active_once_raw(Socket, Data, N-1, []); - NewAcc -> + {ssl, Socket, Info} -> ssl:setopts(Socket, [{active, once}]), - active_once_raw(Socket, Data, N, NewAcc) + active_once_raw(Socket, Data, N, Acc0 ++ Info) end end. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 81f16030f7..cab22a60a8 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -398,7 +398,7 @@ cert_options(Config) -> {ssl_imp, new}]}, {server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, - {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, + %%{server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, {client_psk, [{ssl_imp, new},{reuseaddr, true}, {psk_identity, "Test-User"}, {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, @@ -908,19 +908,8 @@ string_regex_filter(Str, Search) when is_list(Str) -> string_regex_filter(_Str, _Search) -> false. -anonymous_suites() -> - Suites = - [{dh_anon, rc4_128, md5}, - {dh_anon, des_cbc, sha}, - {dh_anon, '3des_ede_cbc', sha}, - {dh_anon, aes_128_cbc, sha}, - {dh_anon, aes_256_cbc, sha}, - {dh_anon, aes_128_gcm, null, sha256}, - {dh_anon, aes_256_gcm, null, sha384}, - {ecdh_anon,rc4_128,sha}, - {ecdh_anon,'3des_ede_cbc',sha}, - {ecdh_anon,aes_128_cbc,sha}, - {ecdh_anon,aes_256_cbc,sha}], +anonymous_suites(Version) -> + Suites = ssl_cipher:anonymous_suites(Version), ssl_cipher:filter_suites(Suites). psk_suites() -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 914eb43505..59732c7926 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 8.0.2 +SSL_VSN = 8.0.3 diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl index ec64470461..5f821caef0 100644 --- a/lib/stdlib/src/edlin_expand.erl +++ b/lib/stdlib/src/edlin_expand.erl @@ -118,7 +118,7 @@ format_col([A|T], Width, Len, Acc0) -> {H1, _} -> H1; H2 -> H2 end, - Acc = [io_lib:format("~-*s", [Width,H]) | Acc0], + Acc = [io_lib:format("~-*ts", [Width,H]) | Acc0], format_col(T, Width, Len+Width, Acc); format_col([], _, _, Acc) -> lists:reverse(Acc, "\n"). diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 9877662743..e917b7ea1f 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -18,9 +18,9 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"3\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.* %% Down to - max one major revision back - [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"3\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.* }. diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index c81e72689c..1cd65fbf18 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -1087,6 +1087,10 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, TRef, EStack); + {'DOWN', _MRef, process, Pid, {shutdown, _}} -> + wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, + TRef, EStack); + {'DOWN', _MRef, process, Pid, normal} when RType =/= permanent -> wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, TRef, EStack); diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl index ccffa2e244..718d91c6a3 100644 --- a/lib/stdlib/test/edlin_expand_SUITE.erl +++ b/lib/stdlib/test/edlin_expand_SUITE.erl @@ -21,7 +21,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2, init_per_group/2,end_per_group/2]). --export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1]). +-export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1, erl_1152/1]). -include_lib("common_test/include/ct.hrl"). @@ -36,7 +36,7 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [normal, quoted_fun, quoted_module, quoted_both]. + [normal, quoted_fun, quoted_module, quoted_both, erl_1152]. groups() -> []. @@ -149,5 +149,12 @@ quoted_both(Config) when is_list(Config) -> {yes,"weird-fun-name'()",[]} = do_expand("'ExpandTestCaps1':'#"), ok. +erl_1152(Config) when is_list(Config) -> + "\n"++"foo"++" "++[1089]++_ = do_format(["foo",[1089]]), + ok. + do_expand(String) -> edlin_expand:expand(lists:reverse(String)). + +do_format(StringList) -> + lists:flatten(edlin_expand:format_matches(StringList)). |