<commit_before>%% -------------------------------------------------------------------
%%
%% Copyright (c) 2013 <NAME>.  All Rights Reserved.
%%
%% This file is provided to you 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.
%%
%% -------------------------------------------------------------------

%% @doc <i>SipApps</i> management module.

-module(nksip_sipapp_config).
-author('<NAME> <<EMAIL>>').

-export([parse_config/1]).

-include("nksip.hrl").

-define(DEFAULT_LOG_LEVEL, 8).  % 8:debug, 7:info, 6:notice, 5:warning, 4:error
-define(DEFAULT_TRACE, false).


%% ===================================================================
%% Private
%% ===================================================================


%% @private
parse_config(Opts) ->
    try
        Opts1 = parse_opts(Opts, []),
        Cache = cache_syntax(Opts1),
        Plugins = nksip_lib:get_value(plugins, Opts1, []),
        Callbacks = callbacks_syntax(Plugins),
        AppName = nksip_lib:get_value(name, Opts1, nksip),
        AppId = nksip_sipapp_srv:get_appid(AppName),
        Module = nksip_lib:get_value(module, Opts1, nksip_sipapp),
        PluginMods = [
            list_to_atom(atom_to_list(Plugin) ++ "_sipapp")
            || Plugin <- Plugins
        ],
        AppCallbacks = get_all_app_callbacks([Module|PluginMods++[nksip_sipapp]]),
        SipApp = [
            nksip_code_util:callback_expr(Mod, Fun, Arity)
            || {{Fun, Arity}, Mod} <- AppCallbacks
        ],
        Syntax = Cache ++ SipApp ++ Callbacks,
        ok = nksip_code_util:compile(AppId, Syntax),
        {ok, AppId} 
    catch
        throw:Throw -> {error, Throw}
    end.


%% @private Parse the list of app start options
parse_opts([], Opts) ->
    Opts;

parse_opts([Term|Rest], Opts) ->
    Opts1 = case Term of

        % Internal options
        {name, Name} ->
            [{name, Name}|Opts];
        {module, Module} when is_atom(Module) ->
            [{module, Module}|Opts];

        % Startup options
        {transports, Transports} ->
            [{transports, parse_transports(Transports, [])}|Opts];
        {certfile, File} ->
            [{certfile, nksip_lib:to_list(File)}|Opts];
        {keyfile, File} ->
            [{keyfile, nksip_lib:to_list(File)}|Opts];
        {supported, Supported} ->
            case nksip_parse:tokens(Supported) of
                error -> throw({invalid, supported});
                Tokens -> [{supported, [T||{T, _}<-Tokens]}|Opts]
            end;
        {allow, Allow} ->
            case nksip_parse:tokens(Allow) of
                error -> throw({invalid, allow});
                Tokens -> [{allow, [A||{A, _}<-Tokens]}|Opts]
            end;
        {accept, Accept} ->
            case nksip_parse:tokens(Accept) of
                error -> throw({invalid, accept});
                Tokens -> [{accept, [A||{A, _}<-Tokens]}|Opts]
            end;
        {events, Event} ->
            case nksip_parse:tokens(Event) of
                error -> throw({invalid, events});
                Tokens -> [{events, [T||{T, _}<-Tokens]}|Opts]
            end;
        
        % Default headers and options
        {from, From} ->
            case nksip_parse:uris(From) of
                [Uri] -> [{from, Uri}|Opts];
                _ -> throw({invalid, from}) 
            end;
        {route, Route} ->
            case nksip_parse:uris(Route) of
                error -> throw({invalid, route});
                Uris -> [{route, Uris}|Opts]
            end;
        {pass, Pass} ->
            Pass1 = case Pass of
                _ when is_list(Pass) -> 
                    list_to_binary(Pass);
                _ when is_binary(Pass) -> 
                    Pass;
                {Pass0, Realm0} when 
                    (is_list(Pass0) orelse is_binary(Pass0)) andalso
                    (is_list(Realm0) orelse is_binary(Realm0)) ->
                    {nksip_lib:to_binary(Pass0), nksip_lib:to_binary(Realm0)};
                _ ->
                    throw({invalid_pass})
            end,
            OldPass = nksip_lib:get_value(passes, Opts, []),
            NewPass = OldPass++[Pass1],
            nksip_lib:store_value(passes, NewPass, Opts);
        {local_host, Host} ->
            [{local_host, nksip_lib:to_host(Host)}|Opts];
        {local_host6, Host} ->
            case nksip_lib:to_ip(Host) of
                {ok, HostIp6} -> 
                    % Ensure it is enclosed in `[]'
                    [{local_host6, nksip_lib:to_host(HostIp6, true)}|Opts];
                error -> 
                    [{local_host6, nksip_lib:to_binary(Host)}|Opts]
            end;
        no_100 ->
            [{no_100, true}|Opts];

        {plugins, List} when is_list(List) ->
            [{plugins, parse_plugins(List, [])}|Opts];

        {log_level, debug} -> [{log_level, 8}|Opts];
        {log_level, info} -> [{log_level, 7}|Opts];
        {log_level, notice} -> [{log_level, 6}|Opts];
        {log_level, warning} -> [{log_level, 5}|Opts];
        {log_level, error} -> [{log_level, 4}|Opts];
        {log_level, critical} -> [{log_level, 3}|Opts];
        {log_level, alert} -> [{log_level, 2}|Opts];
        {log_level, emergency} -> [{log_level, 1}|Opts];
        {log_level, none} -> [{log_level, 0}|Opts];
        {log_level, Level} when Level>=0, Level=<8 -> [{log_level, Level}|Opts];

        {register, Register} ->
            case nksip_parse:uris(Register) of
                error -> throw(invalid_register);
                Uris -> [{register, Uris}|Opts]
            end;
        {register_expires, Expires} when is_integer(Expires), Expires>0 ->
            [{register_expires, Expires}|Opts];
        registrar ->
            [{registrar, true}|Opts];
        {trace, Trace} when is_boolean(Trace) ->
            [{trace, Trace}|Opts];
        {store_trace, Trace} when is_boolean(Trace) ->
            [{store_trace, Trace}|Opts];

        % Unknown options
        {Name, Value} ->
            case nksip_config:parse_config(Name, Value) of
                {ok, Value1} -> 
                    nksip_lib:store_value(Name, Value1, Opts);
                {error, _} ->
                    PlugList = lists:reverse(nksip_lib:get_value(plugins, Opts, [])),
                    case parse_external_opt({Name, Value}, PlugList) of
                        {ok, Value1} -> nksip_lib:store_value(Name, Value1, Opts);
                        _ -> throw({invalid, Name})
                    end
            end;
        Name ->
            PlugList = lists:reverse(nksip_lib:get_value(plugins, Opts, [])),
            case parse_external_opt(Name, PlugList) of
                {ok, Value1} -> nksip_lib:store_value(Name, Value1, Opts);
                _ -> throw({invalid, Name})
            end

    end,
    parse_opts(Rest, Opts1).


%% @doc
parse_external_opt(_Opt, []) ->
    error;

parse_external_opt(Opt, [Plugin|Rest]) ->
    case catch Plugin:parse_config(Opt) of
        {ok, Value} -> {ok, Value};
        _ -> parse_external_opt(Opt, Rest)
    end.


%% @private
parse_transports([], Acc) ->
    lists:reverse(Acc);

parse_transports([Transport|Rest], Acc) -><commit_msg>Process of single element configs<commit_after>%% -------------------------------------------------------------------
%%
%% Copyright (c) 2013 <NAME>.  All Rights Reserved.
%%
%% This file is provided to you 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.
%%
%% -------------------------------------------------------------------

%% @doc <i>SipApps</i> management module.

-module(nksip_sipapp_config).
-author('<NAME> <<EMAIL>>').

-export([parse_config/1]).

-include("nksip.hrl").

-define(DEFAULT_LOG_LEVEL, 8).  % 8:debug, 7:info, 6:notice, 5:warning, 4:error
-define(DEFAULT_TRACE, false).


%% ===================================================================
%% Private
%% ===================================================================


%% @private
parse_config(Opts) ->
    try
        Opts1 = parse_opts(Opts, []),
        Cache = cache_syntax(Opts1),
        Plugins = nksip_lib:get_value(plugins, Opts1, []),
        Callbacks = callbacks_syntax(Plugins),
        AppName = nksip_lib:get_value(name, Opts1, nksip),
        AppId = nksip_sipapp_srv:get_appid(AppName),
        Module = nksip_lib:get_value(module, Opts1, nksip_sipapp),
        PluginMods = [
            list_to_atom(atom_to_list(Plugin) ++ "_sipapp")
            || Plugin <- Plugins
        ],
        AppCallbacks = get_all_app_callbacks([Module|PluginMods++[nksip_sipapp]]),
        SipApp = [
            nksip_code_util:callback_expr(Mod, Fun, Arity)
            || {{Fun, Arity}, Mod} <- AppCallbacks
        ],
        Syntax = Cache ++ SipApp ++ Callbacks,
        ok = nksip_code_util:compile(AppId, Syntax),
        {ok, AppId} 
    catch
        throw:Throw -> {error, Throw}
    end.


%% @private Parse the list of app start options
parse_opts([], Opts) ->
    Opts;

parse_opts([Term|Rest], Opts) ->
    Opts1 = case Term of

        % Internal options
        {name, Name} ->
            [{name, Name}|Opts];
        {module, Module} when is_atom(Module) ->
            [{module, Module}|Opts];

        % Startup options
        {transports, Transports} ->
            [{transports, parse_transports(Transports, [])}|Opts];
        {certfile, File} ->
            [{certfile, nksip_lib:to_list(File)}|Opts];
        {keyfile, File} ->
            [{keyfile, nksip_lib:to_list(File)}|Opts];
        {supported, Supported} ->
            case nksip_parse:tokens(Supported) of
                error -> throw({invalid, supported});
                Tokens -> [{supported, [T||{T, _}<-Tokens]}|Opts]
            end;
        {allow, Allow} ->
            case nksip_parse:tokens(Allow) of
                error -> throw({invalid, allow});
                Tokens -> [{allow, [A||{A, _}<-Tokens]}|Opts]
            end;
        {accept, Accept} ->
            case nksip_parse:tokens(Accept) of
                error -> throw({invalid, accept});
                Tokens -> [{accept, [A||{A, _}<-Tokens]}|Opts]
            end;
        {events, Event} ->
            case nksip_parse:tokens(Event) of
                error -> throw({invalid, events});
                Tokens -> [{events, [T||{T, _}<-Tokens]}|Opts]
            end;
        
        % Default headers and options
        {from, From} ->
            case nksip_parse:uris(From) of
                [Uri] -> [{from, Uri}|Opts];
                _ -> throw({invalid, from}) 
            end;
        {route, Route} ->
            case nksip_parse:uris(Route) of
                error -> throw({invalid, route});
                Uris -> [{route, Uris}|Opts]
            end;
        {pass, Pass} ->
            Pass1 = case Pass of
                _ when is_list(Pass) -> 
                    list_to_binary(Pass);
                _ when is_binary(Pass) -> 
                    Pass;
                {Pass0, Realm0} when 
                    (is_list(Pass0) orelse is_binary(Pass0)) andalso
                    (is_list(Realm0) orelse is_binary(Realm0)) ->
                    {nksip_lib:to_binary(Pass0), nksip_lib:to_binary(Realm0)};
                _ ->
                    throw({invalid_pass})
            end,
            OldPass = nksip_lib:get_value(passes, Opts, []),
            NewPass = OldPass++[Pass1],
            nksip_lib:store_value(passes, NewPass, Opts);
        {local_host, Host} ->
            [{local_host, nksip_lib:to_host(Host)}|Opts];
        {local_host6, Host} ->
            case nksip_lib:to_ip(Host) of
                {ok, HostIp6} -> 
                    % Ensure it is enclosed in `[]'
                    [{local_host6, nksip_lib:to_host(HostIp6, true)}|Opts];
                error -> 
                    [{local_host6, nksip_lib:to_binary(Host)}|Opts]
            end;
        {no_100, true} ->
            [{no_100, true}|Opts];

        {plugins, List} when is_list(List) ->
            [{plugins, parse_plugins(List, [])}|Opts];

        {log_level, debug} -> [{log_level, 8}|Opts];
        {log_level, info} -> [{log_level, 7}|Opts];
        {log_level, notice} -> [{log_level, 6}|Opts];
        {log_level, warning} -> [{log_level, 5}|Opts];
        {log_level, error} -> [{log_level, 4}|Opts];
        {log_level, critical} -> [{log_level, 3}|Opts];
        {log_level, alert} -> [{log_level, 2}|Opts];
        {log_level, emergency} -> [{log_level, 1}|Opts];
        {log_level, none} -> [{log_level, 0}|Opts];
        {log_level, Level} when Level>=0, Level=<8 -> [{log_level, Level}|Opts];

        {register, Register} ->
            case nksip_parse:uris(Register) of
                error -> throw(invalid_register);
                Uris -> [{register, Uris}|Opts]
            end;
        {register_expires, Expires} when is_integer(Expires), Expires>0 ->
            [{register_expires, Expires}|Opts];
        {registrar, true} ->
            [{registrar, true}|Opts];
        {trace, Trace} when is_boolean(Trace) ->
            [{trace, Trace}|Opts];
        {store_trace, Trace} when is_boolean(Trace) ->
            [{store_trace, Trace}|Opts];

        % Unknown options
        Name when is_atom(Name) ->
            parse_opts([{Name, true}|Rest], Opts);
        {Name, Value} ->
            case nksip_config:parse_config(Name, Value) of
                {ok, Value1} -> 
                    nksip_lib:store_value(Name, Value1, Opts);
                {error, _} ->
                    PlugList = lists:reverse(nksip_lib:get_value(plugins, Opts, [])),
                    case parse_external_opt({Name, Value}, PlugList) of
                        {ok, Value1} -> nksip_lib:store_value(Name, Value1, Opts);
                        _ -> throw({invalid, Name})
                    end
            end;
        Other ->
            throw({invalid, Other})
    end,
    parse_opts(Rest, Opts1).


%% @doc
parse_external_opt(_Opt, []) ->
    error;

parse_external_opt(Opt, [Plugin|Rest]) ->
    case catch Plugin:parse_config(Opt) of
        {ok, Value} -> {ok, Value};
        _ -> parse_external_opt(Opt, Rest)
    end.


%% @private
parse_transports([], Acc) ->
    lists:reverse(Acc);

parse_transports([Transport|Rest], Acc) ->
{'fraction_non_alphanumeric': 0.17383866298197131, 'fraction_numerical': 0.007895775759968417, 'mean_word_length': 1.9273882896764252, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 156, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6172472', 'n_tokens_mistral': 4755, 'n_tokens_neox': 4618, 'n_words': 997}