diff options
author | Christophe Romain <christophe.romain@process-one.net> | 2017-09-27 11:12:09 +0200 |
---|---|---|
committer | Christophe Romain <christophe.romain@process-one.net> | 2017-09-27 11:12:09 +0200 |
commit | c1d3d1318e40bd9a22525e2ae44f17aedc728269 (patch) | |
tree | bef342f80b78c4db624a00ffbd656432abec2445 | |
parent | PubSub: add correct order when requesting all items (diff) | |
parent | Add support for XEP-0368: SRV records for XMPP over TLS (diff) |
Merge branch 'master' of github.com:processone/ejabberd
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | contrib/extract_translations/README | 21 | ||||
-rw-r--r-- | contrib/extract_translations/extract_translations.erl | 307 | ||||
-rwxr-xr-x | contrib/extract_translations/prepare-translation.sh | 366 | ||||
-rw-r--r-- | rebar.config | 2 | ||||
-rw-r--r-- | src/mod_http_upload.erl | 2 | ||||
-rw-r--r-- | src/xmpp_stream_out.erl | 91 | ||||
-rwxr-xr-x | tools/prepare-tr.sh | 100 |
8 files changed, 168 insertions, 723 deletions
diff --git a/Makefile.in b/Makefile.in index 3bee2f649..76c38043a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -102,7 +102,7 @@ xref: all translations: - contrib/extract_translations/prepare-translation.sh -updateall + tools/prepare-tr.sh edoc: $(ERL) -noinput +B -eval \ diff --git a/contrib/extract_translations/README b/contrib/extract_translations/README deleted file mode 100644 index 9278dd106..000000000 --- a/contrib/extract_translations/README +++ /dev/null @@ -1,21 +0,0 @@ -extract_translations - auxiliary tool that extracts lines to be translated -from ejabberd source tree. - -Building: - erlc extract_translations.erl - -Invoking 1: - erl -noinput -s extract_translations -extra dirname message_file - - where dirname is the directory "src" in ejabberd's source tree root, - message_file is a file with translated messages (src/msgs/*.msg). - - Result is a list of messages from source files which aren't contained in - message file. - -Invoking 2: - erl -noinput -s extract_translations -extra -unused dirname message_file - - Result is a list of messages from message file which aren't in source - files anymore. - diff --git a/contrib/extract_translations/extract_translations.erl b/contrib/extract_translations/extract_translations.erl deleted file mode 100644 index 70304761a..000000000 --- a/contrib/extract_translations/extract_translations.erl +++ /dev/null @@ -1,307 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : extract_translations.erl -%%% Author : Sergei Golovan <sgolovan@nes.ru> -%%% Purpose : Auxiliary tool for interface/messages translators -%%% Created : 23 Apr 2005 by Sergei Golovan <sgolovan@nes.ru> -%%% Id : $Id$ -%%%---------------------------------------------------------------------- - --module(extract_translations). --author('sgolovan@nes.ru'). - --export([start/0]). - --define(STATUS_SUCCESS, 0). --define(STATUS_ERROR, 1). --define(STATUS_USAGE, 2). - --include_lib("kernel/include/file.hrl"). - - -start() -> - ets:new(translations, [named_table, public]), - ets:new(translations_obsolete, [named_table, public]), - ets:new(files, [named_table, public]), - ets:new(vars, [named_table, public]), - case init:get_plain_arguments() of - ["-srcmsg2po", Dir, File] -> - print_po_header(File), - Status = process(Dir, File, srcmsg2po), - halt(Status); - ["-unused", Dir, File] -> - Status = process(Dir, File, unused), - halt(Status); - [Dir, File] -> - Status = process(Dir, File, used), - halt(Status); - _ -> - print_usage(), - halt(?STATUS_USAGE) - end. - - -process(Dir, File, Used) -> - case load_file(File) of - {error, Reason} -> - io:format("~s: ~s~n", [File, file:format_error(Reason)]), - ?STATUS_ERROR; - _ -> - FileList = find_src_files(Dir), - lists:foreach( - fun(F) -> - parse_file(Dir, F, Used) - end, FileList), - case Used of - unused -> - ets:foldl(fun({Key, _}, _) -> - io:format("~p~n", [Key]) - end, ok, translations); - srcmsg2po -> - ets:foldl(fun({Key, Trans}, _) -> - print_translation_obsolete(Key, Trans) - end, ok, translations_obsolete); - _ -> - ok - end, - ?STATUS_SUCCESS - end. - -parse_file(Dir, File, Used) -> - ets:delete_all_objects(vars), - case epp:parse_file(File, [Dir, filename:dirname(File) | code:get_path()], []) of - {ok, Forms} -> - lists:foreach( - fun(F) -> - parse_form(Dir, File, F, Used) - end, Forms); - _ -> - ok - end. - -parse_form(Dir, File, Form, Used) -> - case Form of - %%{undefined, Something} -> - %% io:format("Undefined: ~p~n", [Something]); - {call, - _, - {remote, _, {atom, _, translate}, {atom, _, translate}}, - [_, {string, Line, Str}] - } -> - process_string(Dir, File, Line, Str, Used); - {call, - _, - {remote, _, {atom, _, translate}, {atom, _, translate}}, - [_, - {bin,_, - [{bin_element,_, - {string,Line,Str}, - default,default}]}] - } -> - process_string(Dir, File, Line, Str, Used); - {call, - _, - {remote, _, {atom, _, translate}, {atom, _, translate}}, - [_, {var, _, Name}] - } -> - case ets:lookup(vars, Name) of - [{_Name, Value, Line}] -> - process_string(Dir, File, Line, Value, Used); - _ -> - ok - end; - {match, - _, - {var, _, Name}, - {string, Line, Value} - } -> - ets:insert(vars, {Name, Value, Line}); - {match, - _, - {var, _, Name}, - {bin,Line,[{bin_element,_,{string,_,Value},_,_}]} - } -> - ets:insert(vars, {Name, Value, Line}); - L when is_list(L) -> - lists:foreach( - fun(F) -> - parse_form(Dir, File, F, Used) - end, L); - T when is_tuple(T) -> - lists:foreach( - fun(F) -> - parse_form(Dir, File, F, Used) - end, tuple_to_list(T)); - _ -> - ok - end. - -process_string(_Dir, _File, _Line, "", _Used) -> - ok; - -process_string(_Dir, File, Line, Str, Used) -> - case {ets:lookup(translations, Str), Used} of - {[{_Key, _Trans}], unused} -> - ets:delete(translations, Str); - {[{_Key, _Trans}], used} -> - ok; - {[{_Key, Trans}], srcmsg2po} -> - ets:delete(translations_obsolete, Str), - print_translation(File, Line, Str, Trans); - {_, used} -> - case ets:lookup(files, File) of - [{_}] -> - ok; - _ -> - io:format("~n% ~s~n", [File]), - ets:insert(files, {File}) - end, - case Str of - [] -> ok; - _ -> io:format("{~p, \"\"}.~n", [Str]) - end, - ets:insert(translations, {Str, ""}); - {_, srcmsg2po} -> - case ets:lookup(files, File) of - [{_}] -> - ok; - _ -> - ets:insert(files, {File}) - end, - ets:insert(translations, {Str, ""}), - print_translation(File, Line, Str, ""); - _ -> - ok - end. - -load_file(File) -> - case file:consult(File) of - {ok, Terms} -> - lists:foreach( - fun({Orig, Trans}) -> - case Trans of - "" -> - ok; - _ -> - ets:insert(translations, {Orig, Trans}), - ets:insert(translations_obsolete, {Orig, Trans}) - end - end, Terms); - Err -> - Err - end. - -find_src_files(Dir) -> - case file:list_dir(Dir) of - {ok, FileList} -> - recurse_filelist( - lists:map( - fun(F) -> - filename:join(Dir, F) - end, FileList)); - _ -> - [] - end. - -recurse_filelist(FileList) -> - recurse_filelist(FileList, []). - -recurse_filelist([], Acc) -> - lists:reverse(Acc); - -recurse_filelist([H | T], Acc) -> - case file:read_file_info(H) of - {ok, #file_info{type = directory}} -> - recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc); - {ok, #file_info{type = regular}} -> - case string:substr(H, string:len(H) - 3) of - ".erl" -> - recurse_filelist(T, [H | Acc]); - ".hrl" -> - recurse_filelist(T, [H | Acc]); - _ -> - recurse_filelist(T, Acc) - end; - _ -> - recurse_filelist(T, Acc) - end. - - -print_usage() -> - io:format( - "Usage: extract_translations [-unused] dir file~n" - "~n" - "Example:~n" - " extract_translations . ./msgs/ru.msg~n" - ). - - -%%% -%%% Gettext -%%% - -print_po_header(File) -> - MsgProps = get_msg_header_props(File), - {Language, [LastT | AddT]} = prepare_props(MsgProps), - print_po_header(Language, LastT, AddT). - -get_msg_header_props(File) -> - {ok, F} = file:open(File, [read]), - Lines = get_msg_header_props(F, []), - file:close(F), - Lines. - -get_msg_header_props(F, Lines) -> - String = io:get_line(F, ""), - case io_lib:fread("% ", String) of - {ok, [], RemString} -> - case io_lib:fread("~s", RemString) of - {ok, [Key], Value} when Value /= "\n" -> - %% The first character in Value is a blankspace: - %% And the last characters are 'slash n' - ValueClean = string:substr(Value, 2, string:len(Value)-2), - get_msg_header_props(F, Lines ++ [{Key, ValueClean}]); - _ -> - get_msg_header_props(F, Lines) - end; - _ -> - Lines - end. - -prepare_props(MsgProps) -> - Language = proplists:get_value("Language:", MsgProps), - Authors = proplists:get_all_values("Author:", MsgProps), - {Language, Authors}. - -print_po_header(Language, LastTranslator, AdditionalTranslatorsList) -> - AdditionalTranslatorsString = build_additional_translators(AdditionalTranslatorsList), - HeaderString = - "msgid \"\"\n" - "msgstr \"\"\n" - ++ "\"X-Language: " ++ Language ++ "\\n\"\n" - "\"Last-Translator: " ++ LastTranslator ++ "\\n\"\n" - ++ AdditionalTranslatorsString ++ - "\"MIME-Version: 1.0\\n\"\n" - "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" - "\"Content-Transfer-Encoding: 8bit\\n\"\n", - io:format("~s~n", [HeaderString]). - -build_additional_translators(List) -> - lists:foldl( - fun(T, Str) -> - Str ++ "\"X-Additional-Translator: " ++ T ++ "\\n\"\n" - end, - "", - List). - -print_translation(File, Line, Str, StrT) -> - StrQ = ejabberd_regexp:greplace(list_to_binary(Str), <<"\\\"">>, <<"\\\\\"">>), - StrTQ = ejabberd_regexp:greplace(list_to_binary(StrT), <<"\\\"">>, <<"\\\\\"">>), - io:format("#: ~s:~p~nmsgid \"~s\"~nmsgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]). - -print_translation_obsolete(Str, StrT) -> - File = "unknown.erl", - Line = 1, - StrQ = ejabberd_regexp:greplace(Str, "\\\"", "\\\\\""), - StrTQ = ejabberd_regexp:greplace(StrT, "\\\"", "\\\\\""), - io:format("#: ~s:~p~n#~~ msgid \"~s\"~n#~~ msgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]). - diff --git a/contrib/extract_translations/prepare-translation.sh b/contrib/extract_translations/prepare-translation.sh deleted file mode 100755 index 98c282cc1..000000000 --- a/contrib/extract_translations/prepare-translation.sh +++ /dev/null @@ -1,366 +0,0 @@ -#!/bin/bash - -# Frontend for ejabberd's extract_translations.erl -# by Badlop - -# How to create template files for a new language: -# NEWLANG=zh -# cp msgs/ejabberd.pot msgs/$NEWLANG.po -# echo \{\"\",\"\"\}. > msgs/$NEWLANG.msg -# ../../extract_translations/prepare-translation.sh -updateall - -prepare_dirs () -{ - # Where is Erlang binary - ERL=`which erl` - - EJA_SRC_DIR=$EJA_DIR/src/ - EJA_MSGS_DIR=$EJA_DIR/priv/msgs/ - EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/ - EXTRACT_ERL=$EXTRACT_DIR/extract_translations.erl - EXTRACT_BEAM=$EXTRACT_DIR/extract_translations.beam - - SRC_DIR=$RUN_DIR/src - EBIN_DIR=$RUN_DIR/ebin - MSGS_DIR=$EJA_DIR/priv/msgs - - if !([[ -n $EJA_DIR ]]) - then - echo "ejabberd dir does not exist: $EJA_DIR" - fi - - if !([[ -x $EXTRACT_BEAM ]]) - then - sh -c "cd $EXTRACT_DIR; $ERL -compile $EXTRACT_ERL" - fi -} - -extract_lang () -{ - MSGS_FILE=$1 - MSGS_FILE2=$MSGS_FILE.translate - MSGS_PATH=$MSGS_DIR/$MSGS_FILE - MSGS_PATH2=$MSGS_DIR/$MSGS_FILE2 - - echo -n "Extracting language strings for '$MSGS_FILE':" - - echo -n " new..." - cd $SRC_DIR - $ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra . $MSGS_PATH >$MSGS_PATH.new - sed -e 's/^% \.\//% /g;' $MSGS_PATH.new > $MSGS_PATH.new2 - mv $MSGS_PATH.new2 $MSGS_PATH.new - - echo -n " old..." - $ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra -unused . $MSGS_PATH >$MSGS_PATH.unused - find_unused_full $MSGS_FILE $MSGS_FILE.unused - - echo "" >$MSGS_PATH2 - echo " ***** Translation file for ejabberd ***** " >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - - echo "" >>$MSGS_PATH2 - echo " *** New strings: Can you please translate them? *** " >>$MSGS_PATH2 - cat $MSGS_PATH.new >>$MSGS_PATH2 - - echo "" >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - echo " *** Unused strings: They will be removed automatically *** " >>$MSGS_PATH2 - cat $MSGS_PATH.unused.full >>$MSGS_PATH2 - - echo "" >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - echo " *** Already translated strings: you can also modify any of them if you want *** " >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - cat $MSGS_PATH.old_cleaned >>$MSGS_PATH2 - - echo " ok" - - rm $MSGS_PATH.new - rm $MSGS_PATH.old_cleaned - rm $MSGS_PATH.unused.full -} - -extract_lang_all () -{ - cd $MSGS_DIR - for i in $( ls *.msg ) ; do - extract_lang $i; - done - - echo -e "File\tMissing\tLanguage\t\tLast translator" - echo -e "----\t-------\t--------\t\t---------------" - cd $MSGS_DIR - for i in $( ls *.msg ) ; do - MISSING=`cat $i.translate | grep "\", \"\"}." | wc -l` - LANGUAGE=`grep "X-Language:" $i.translate | sed 's/% Language: //g'` - LASTAUTH=`grep "Author:" $i.translate | head -n 1 | sed 's/% Author: //g'` - echo -e "$i\t$MISSING\t$LANGUAGE\t$LASTAUTH" - done - - cd $MSGS_DIR - REVISION=`git describe --always` - zip $HOME/ejabberd-langs-$REVISION.zip *.translate; - - rm *.translate -} - -find_unused_full () -{ - DATFILE=$1 - DATFILEI=$1.old_cleaned - DELFILE=$2 - cd msgs - - DATFILE1=$DATFILE.t1 - DATFILE2=$DATFILE.t2 - - DELFILE1=$DELFILE.t1 - DELFILE2=$DELFILE.t2 - DELFILEF=$DATFILE.unused.full - echo "" >$DELFILEF - - grep -v "\\\\" $DELFILE >$DELFILE2 - echo ENDFILEMARK >>$DELFILE2 - cp $DATFILE $DATFILEI - cp $DATFILE $DATFILE2 - - cp $DELFILE2 $DELFILE1 - STRING=`head -1 $DELFILE1` - until [[ $STRING == ENDFILEMARK ]]; do - cp $DELFILE2 $DELFILE1 - cp $DATFILE2 $DATFILE1 - - STRING=`head -1 $DELFILE1` - - cat $DATFILE1 | grep "$STRING" >>$DELFILEF - cat $DATFILE1 | grep -v "$STRING" >$DATFILE2 - cat $DELFILE1 | grep -v "$STRING" >$DELFILE2 - done - - mv $DATFILE2 $DATFILEI - - rm -f $MSGS_PATH.t1 - rm $MSGS_PATH.unused - rm -f $MSGS_PATH.unused.t1 - rm $MSGS_PATH.unused.t2 - - cd .. -} - -extract_lang_srcmsg2po () -{ - LANG=$1 - LANG_CODE=$LANG.$PROJECT - MSGS_PATH=$MSGS_DIR/$LANG_CODE.msg - PO_PATH=$MSGS_DIR/$LANG_CODE.po - - echo $MSGS_PATH - - cd $SRC_DIR - $ERL -pa $EXTRACT_DIR -pa $EBIN_DIR -pa $EJA_SRC_DIR -pa ../include -noinput -noshell -s extract_translations -s init stop -extra -srcmsg2po . $MSGS_PATH >$PO_PATH.1 - sed -e 's/ \[\]$/ \"\"/g;' $PO_PATH.1 > $PO_PATH.2 - msguniq --sort-by-file $PO_PATH.2 --output-file=$PO_PATH - - rm $PO_PATH.* -} - -extract_lang_src2pot () -{ - LANG_CODE=$PROJECT - MSGS_PATH=$MSGS_DIR/$LANG_CODE.msg - POT_PATH=$MSGS_DIR/$LANG_CODE.pot - - echo -n "" >$MSGS_PATH - echo "% Language: Language Name" >>$MSGS_PATH - echo "% Author: Translator name and contact method" >>$MSGS_PATH - echo "" >>$MSGS_PATH - - cd $SRC_DIR - $ERL -pa $EXTRACT_DIR -pa $EBIN_DIR -pa $EJA_SRC_DIR -pa ../include -noinput -noshell -s extract_translations -s init stop -extra -srcmsg2po . $MSGS_PATH >$POT_PATH.1 - sed -e 's/ \[\]$/ \"\"/g;' $POT_PATH.1 > $POT_PATH.2 - - #msguniq --sort-by-file $POT_PATH.2 $EJA_MSGS_DIR --output-file=$POT_PATH - msguniq --sort-by-file $POT_PATH.2 --output-file=$POT_PATH - - rm $POT_PATH.* - rm $MSGS_PATH - - # If the project is a specific module, not the main ejabberd - if [[ $PROJECT != ejabberd ]] ; then - # Remove from project.pot the strings that are already present in the general ejabberd - EJABBERD_MSG_FILE=$EJA_MSGS_DIR/es.po # This is just some file with translated strings - POT_PATH_TEMP=$POT_PATH.temp - msgattrib --set-obsolete --only-file=$EJABBERD_MSG_FILE -o $POT_PATH_TEMP $POT_PATH - mv $POT_PATH_TEMP $POT_PATH - fi -} - -extract_lang_popot2po () -{ - LANG_CODE=$1 - PO_PATH=$MSGS_DIR/$LANG_CODE.po - POT_PATH=$MSGS_DIR/$PROJECT.pot - - msgmerge $PO_PATH $POT_PATH >$PO_PATH.translate 2>/dev/null - mv $PO_PATH.translate $PO_PATH -} - -extract_lang_po2msg () -{ - LANG_CODE=$1 - PO_PATH=$LANG_CODE.po - MS_PATH=$PO_PATH.ms - MSGID_PATH=$PO_PATH.msgid - MSGSTR_PATH=$PO_PATH.msgstr - MSGS_PATH=$LANG_CODE.msg - - cd $MSGS_DIR - - # Check PO has correct ~ - # Let's convert to C format so we can use msgfmt - PO_TEMP=$LANG_CODE.po.temp - cat $PO_PATH | sed 's/%/perc/g' | sed 's/~/%/g' | sed 's/#:.*/#, c-format/g' >$PO_TEMP - msgfmt $PO_TEMP --check-format - result=$? - rm $PO_TEMP - if [ $result -ne 0 ] ; then - exit 1 - fi - - msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH - grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH - grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH - echo "%% -*- coding: latin-1 -*-" >$MSGS_PATH - paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH - - rm $MS_PATH - rm $MSGID_PATH - rm $MSGSTR_PATH -} - -extract_lang_updateall () -{ - echo "Generating POT" - extract_lang_src2pot - - cd $MSGS_DIR - echo "" - echo -e "File Missing Language Last translator" - echo -e "---- ------- -------- ---------------" - for i in $( ls *.msg ) ; do - LANG_CODE=${i%.msg} - echo -n $LANG_CODE | awk '{printf "%-6s", $1 }' - - # Convert old MSG file to PO - PO=$LANG_CODE.po - [ -f $PO ] || extract_lang_srcmsg2po $LANG_CODE - - extract_lang_popot2po $LANG_CODE - extract_lang_po2msg $LANG_CODE - - MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4 }'` - echo -n " $MISSING" - - LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'` - echo -n " $LANGUAGE" - - LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'` - echo " $LASTAUTH" - done - echo "" - rm messages.mo - - cd .. -} - -translation_instructions () -{ - echo "" - echo " A new file has been created for you, with the current, the new and the deprecated strings:" - echo " $MSGS_PATH2" - echo "" - echo " At the end of that file you will find the strings you must update:" - echo " - Untranslated strings are like this: {"March", ""}." - echo " To translate the string, add the text inside the commas. Example:" - echo " {"March", "Marzo"}." - echo " - Old strings that are not used: "Woowoa"" - echo " Search the entire file for those strings and remove them" - echo "" - echo " Once you have translated all the strings and removed all the old ones," - echo " rename the file to overwrite the previous one:" - echo " $MSGS_PATH" -} - -EJA_DIR=`pwd` -RUN_DIR=`pwd` -PROJECT=ejabberd - -while [ $# -ne 0 ] ; do - PARAM=$1 - shift - case $PARAM in - --) break ;; - -project) - PROJECT=$1 - shift - ;; - -ejadir) - EJA_DIR=$1 - shift - ;; - -rundir) - RUN_DIR=$1 - shift - ;; - -lang) - LANGU=$1 - prepare_dirs - extract_lang $LANGU - shift - ;; - -langall) - prepare_dirs - extract_lang_all - ;; - -srcmsg2po) - LANG_CODE=$1 - prepare_dirs - extract_lang_srcmsg2po $LANG_CODE - shift - ;; - -popot2po) - LANG_CODE=$1 - prepare_dirs - extract_lang_popot2po $LANG_CODE - shift - ;; - -src2pot) - prepare_dirs - extract_lang_src2pot - ;; - -po2msg) - LANG_CODE=$1 - prepare_dirs - extract_lang_po2msg $LANG_CODE - shift - ;; - -updateall) - prepare_dirs - extract_lang_updateall - ;; - *) - echo "Options:" - echo " -langall" - echo " -lang LANGUAGE_FILE" - echo " -srcmsg2po LANGUAGE Construct .msg file using source code to PO file" - echo " -src2pot Generate template POT file from source code" - echo " -popot2po LANGUAGE Update PO file with template POT file" - echo " -po2msg LANGUAGE Export PO file to MSG file" - echo " -updateall Generate POT and update all PO" - echo "" - echo "Example:" - echo " ./prepare-translation.sh -lang es.msg" - exit 0 - ;; - esac -done diff --git a/rebar.config b/rebar.config index 0744a61c4..d9aaae597 100644 --- a/rebar.config +++ b/rebar.config @@ -22,7 +22,7 @@ {tag, {if_version_above, "17", "3.4.2", "3.2.1"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.9"}}}, {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.10"}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.15"}}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "fc3ef32"}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.9"}}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.23"}}}, {xmpp, ".*", {git, "https://github.com/processone/xmpp", "d98be4a3159"}}, diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 7e037da4d..c3c295f66 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -25,7 +25,7 @@ -module(mod_http_upload). -author('holger@zedat.fu-berlin.de'). --compile(export_all). + -protocol({xep, 363, '0.1'}). -define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds. diff --git a/src/xmpp_stream_out.erl b/src/xmpp_stream_out.erl index af5c67c66..024fdf63b 100644 --- a/src/xmpp_stream_out.erl +++ b/src/xmpp_stream_out.erl @@ -51,13 +51,15 @@ -type host_port() :: {inet:hostname(), inet:port_number()}. -type ip_port() :: {inet:ip_address(), inet:port_number()}. -type network_error() :: {error, inet:posix() | inet_res:res_error()}. +-type tls_error_reason() :: inet:posix() | atom() | binary(). +-type socket_error_reason() :: inet:posix() | atom(). -type stop_reason() :: {idna, bad_string} | {dns, inet:posix() | inet_res:res_error()} | {stream, reset | {in | out, stream_error()}} | - {tls, inet:posix() | atom() | binary()} | + {tls, tls_error_reason()} | {pkix, binary()} | {auth, atom() | binary() | string()} | - {socket, inet:posix() | atom()} | + {socket, socket_error_reason()} | internal_failure. -export_type([state/0, stop_reason/0]). -callback init(list()) -> {ok, state()} | {error, term()} | ignore. @@ -276,19 +278,20 @@ handle_cast(connect, #{remote_server := RemoteServer, process_stream_end({idna, bad_string}, State); ASCIIName -> case resolve(binary_to_list(ASCIIName), State) of - {ok, AddrPorts} -> - case connect(AddrPorts, State) of + {{ok, AddrPorts}, Encrypted} -> + case connect(AddrPorts, State, Encrypted) of {ok, Socket, AddrPort} -> SocketMonitor = SockMod:monitor(Socket), State1 = State#{ip => AddrPort, socket => Socket, + stream_encrypted => Encrypted, socket_monitor => SocketMonitor}, State2 = State1#{stream_state => wait_for_stream}, send_header(State2); - {error, Why} -> - process_stream_end({socket, Why}, State) + {error, {Class, Why}} -> + process_stream_end({Class, Why}, State) end; - {error, Why} -> + {{error, Why}, _} -> process_stream_end({dns, Why}, State) end end); @@ -578,11 +581,8 @@ process_sasl_mechanisms(Mechs, #{user := User, server := Server} = State) -> end. -spec process_starttls(state()) -> state(). -process_starttls(#{sockmod := SockMod, socket := Socket, mod := Mod} = State) -> - TLSOpts = try Mod:tls_options(State) - catch _:undef -> [] - end, - case SockMod:starttls(Socket, [connect|TLSOpts]) of +process_starttls(#{socket := Socket} = State) -> + case starttls(Socket, State) of {ok, TLSSocket} -> State1 = State#{socket => TLSSocket, stream_id => new_id(), @@ -770,6 +770,19 @@ close_socket(State) -> State#{stream_timeout => infinity, stream_state => disconnected}. +-spec starttls(term(), state()) -> {ok, term()} | {error, tls_error_reason()}. +starttls(Socket, #{sockmod := SockMod, mod := Mod, + xmlns := NS, remote_server := RemoteServer} = State) -> + TLSOpts = try Mod:tls_options(State) + catch _:undef -> [] + end, + SNI = idna_to_ascii(RemoteServer), + ALPN = case NS of + ?NS_SERVER -> <<"xmpp-server">>; + ?NS_CLIENT -> <<"xmpp-client">> + end, + SockMod:starttls(Socket, [connect, {sni, SNI}, {alpn, [ALPN]}|TLSOpts]). + -spec select_lang(binary(), binary()) -> binary(). select_lang(Lang, <<"">>) -> Lang; select_lang(_, Lang) -> Lang. @@ -841,17 +854,17 @@ idna_to_ascii(Host) -> {error, _} -> ejabberd_idna:domain_utf8_to_ascii(Host) end. --spec resolve(string(), state()) -> {ok, [ip_port()]} | network_error(). +-spec resolve(string(), state()) -> {{ok, [ip_port()]} | network_error(), boolean()}. resolve(Host, State) -> case srv_lookup(Host, State) of - {error, _Reason} -> + {{error, _Reason}, _} -> DefaultPort = get_default_port(State), - a_lookup([{Host, DefaultPort}], State); - {ok, HostPorts} -> - a_lookup(HostPorts, State) + {a_lookup([{Host, DefaultPort}], State), false}; + {{ok, HostPorts}, TLS} -> + {a_lookup(HostPorts, State), TLS} end. --spec srv_lookup(string(), state()) -> {ok, [host_port()]} | network_error(). +-spec srv_lookup(string(), state()) -> {{ok, [host_port()]} | network_error(), boolean()}. srv_lookup(_Host, #{xmlns := ?NS_COMPONENT}) -> %% Do not attempt to lookup SRV for component connections {error, nxdomain}; @@ -867,21 +880,35 @@ srv_lookup(Host, State) -> {error, _} -> Timeout = get_dns_timeout(State), Retries = get_dns_retries(State), - srv_lookup(Host, Timeout, Retries) + case is_starttls_available(State) of + true -> + case srv_lookup("_xmpps-server._tcp." ++ Host, + Timeout, Retries) of + {error, _} -> + {srv_lookup("_xmpp-server._tcp." ++ Host, + Timeout, Retries), + false}; + {ok, Res} -> + {{ok, Res}, true} + end; + false -> + {srv_lookup("_xmpp-server._tcp." ++ Host, + Timeout, Retries), + false} + end end end. -spec srv_lookup(string(), timeout(), integer()) -> {ok, [host_port()]} | network_error(). -srv_lookup(_Host, _Timeout, Retries) when Retries < 1 -> +srv_lookup(_SRVName, _Timeout, Retries) when Retries < 1 -> {error, timeout}; -srv_lookup(Host, Timeout, Retries) -> - SRVName = "_xmpp-server._tcp." ++ Host, +srv_lookup(SRVName, Timeout, Retries) -> case inet_res:getbyname(SRVName, srv, Timeout) of {ok, HostEntry} -> host_entry_to_host_ports(HostEntry); {error, timeout} -> - srv_lookup(Host, Timeout, Retries - 1); + srv_lookup(SRVName, Timeout, Retries - 1); {error, _} = Err -> Err end. @@ -971,10 +998,22 @@ host_entry_to_addr_ports(#hostent{h_addr_list = AddrList}, Port) -> _ -> {ok, AddrPorts} end. --spec connect([ip_port()], state()) -> {ok, term(), ip_port()} | network_error(). -connect(AddrPorts, #{sockmod := SockMod} = State) -> +-spec connect([ip_port()], state(), boolean()) -> {ok, term(), ip_port()} | + {error, {socket, socket_error_reason()}} | + {error, {tls, tls_error_reason()}}. +connect(AddrPorts, #{sockmod := SockMod} = State, TLS) -> Timeout = get_connect_timeout(State), - connect(AddrPorts, SockMod, Timeout, {error, nxdomain}). + case connect(AddrPorts, SockMod, Timeout, {error, nxdomain}) of + {ok, Socket, AddrPort} when TLS -> + case starttls(Socket, State) of + {ok, TLSSocket} -> {ok, TLSSocket, AddrPort}; + {error, Why} -> {error, {tls, Why}} + end; + {ok, _Socket, _AddrPort} = OK -> + OK; + {error, Why} -> + {error, {socket, Why}} + end. -spec connect([ip_port()], module(), timeout(), network_error()) -> {ok, term(), ip_port()} | network_error(). diff --git a/tools/prepare-tr.sh b/tools/prepare-tr.sh new file mode 100755 index 000000000..8829b273d --- /dev/null +++ b/tools/prepare-tr.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +# Frontend for ejabberd's extract-tr.sh + +# How to create template files for a new language: +# NEWLANG=zh +# cp priv/msgs/ejabberd.pot priv/msgs/$NEWLANG.po +# echo \{\"\",\"\"\}. > priv/msgs/$NEWLANG.msg +# make translations + +extract_lang_src2pot () +{ + ./tools/extract-tr.sh ebin/ > priv/msgs/ejabberd.pot +} + +extract_lang_popot2po () +{ + LANG_CODE=$1 + PO_PATH=$MSGS_DIR/$LANG_CODE.po + POT_PATH=$MSGS_DIR/$PROJECT.pot + + msgmerge $PO_PATH $POT_PATH >$PO_PATH.translate 2>/dev/null + mv $PO_PATH.translate $PO_PATH +} + +extract_lang_po2msg () +{ + LANG_CODE=$1 + PO_PATH=$LANG_CODE.po + MS_PATH=$PO_PATH.ms + MSGID_PATH=$PO_PATH.msgid + MSGSTR_PATH=$PO_PATH.msgstr + MSGS_PATH=$LANG_CODE.msg + + cd $MSGS_DIR + + # Check PO has correct ~ + # Let's convert to C format so we can use msgfmt + PO_TEMP=$LANG_CODE.po.temp + cat $PO_PATH | sed 's/%/perc/g' | sed 's/~/%/g' | sed 's/#:.*/#, c-format/g' >$PO_TEMP + msgfmt $PO_TEMP --check-format + result=$? + rm $PO_TEMP + if [ $result -ne 0 ] ; then + exit 1 + fi + + msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH + grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH + grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH + echo "%% -*- coding: latin-1 -*-" >$MSGS_PATH + paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH + + rm $MS_PATH + rm $MSGID_PATH + rm $MSGSTR_PATH +} + +extract_lang_updateall () +{ + echo "" + echo "Generating POT..." + extract_lang_src2pot + + cd $MSGS_DIR + echo "" + echo -e "File Missing (fuzzy) Language Last translator" + echo -e "---- ------- ------- -------- ---------------" + for i in $( ls *.msg ) ; do + LANG_CODE=${i%.msg} + echo -n $LANG_CODE | awk '{printf "%-6s", $1 }' + + PO=$LANG_CODE.po + + extract_lang_popot2po $LANG_CODE + extract_lang_po2msg $LANG_CODE + + MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4+$7 }'` + echo -n " $MISSING" + + FUZZY=`msgfmt --statistics $PO 2>&1 | awk '{printf "%7s", $4 }'` + echo -n " $FUZZY" + + LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'` + echo -n " $LANGUAGE" + + LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'` + echo " $LASTAUTH" + done + echo "" + rm messages.mo + + cd .. +} + +EJA_DIR=`pwd` +PROJECT=ejabberd +MSGS_DIR=$EJA_DIR/priv/msgs + +extract_lang_updateall |