I try to call a web service using the package UTL_HTTP, it works but I have an issue with french accents( 'é' and 'è') , the UTF-8 is not working .
CONTENT := '{
"metier": {
"REF_CONTRAT": "'||ref_contrat||'",
"ID_HISTO": "'||id_histo||'",
"ID_OBJ_DECLENCHEUR": "'||ID_OBJ_DECLENCHEUR||'",
"TYPE_OBJ_DECLENCHEUR": "'||type_obj_declencheur||'",
"ID_SCENARIO_INSTANCIE": "'||ID_SCENARIO_INSTANCIE||'",
"ID_SCENARIO": "'||id_scenario||'",
"ID_SMS": "'||id_sms||'",
"REPONSE_RECUE": "'||reponse_recue||'",
"ACTION": "'||ACTION||'",
"TYPE_ACTION": "'||TYPE_ACTION||'"
}
}';
-- V_URL:=UTL_URL.ESCAPE(V_URL,FALSE,'UTF-8');
UTL_HTTP.SET_BODY_CHARSET('UTF-8');
REQ := UTL_HTTP.BEGIN_REQUEST(V_URL, V_METHODE,' HTTP/1.1');
UTL_HTTP.SET_HEADER(REQ, 'user-agent', 'mozilla/4.0');
-- UTL_HTTP.SET_HEADER(REQ, 'content-type', V_CONTENT_TYPE);
UTL_HTTP.SET_HEADER(REQ, 'content-type','application/json; charset="UTF-8"');
utl_http.set_header(req, 'Content-Length', lengthB(content));
UTL_HTTP.SET_HEADER(REQ,'Authorization',V_AUTHORIZATION);
UTL_HTTP.WRITE_TEXT(REQ, CONTENT);
/* UTL_HTTP.WRITE_RAW (R => REQ,
data => UTL_RAW.CAST_TO_RAW(CONTENT)); */
I have error 500 as a response.
any idea please ? I tried all what I found on the internet but it's not working
The problem was that length doesn't give the right length when the question contains combined characters ; é was counted as 1 byte instead of 2 so it worked when I converted the request to AL32UTF8 : length(Convert(CONTENT,'AL32UTF8'))
I tried to make work a piece of code that opens an http connection.
Nevertheless, web page may transfered as plain text or gziped.
As a result, the code with pragmatism tries to open as plain text and if it fails and receives an exception, tries as if it is gzip encoded.
URL is the sole variable to ground.
Try with URL = 'http://releases.llvm.org/6.0.0/tools/clang/docs/ClangCommandLineReference.html' for instance.
user::catch(
(
user::http_open(URL, DataStream, []),
user::load_html(stream(DataStream), Terms, []),
user::close(DataStream)
),
_
,
(
user::open_any(URL, read, GZipDataStream, CloseIt, [encoding(gzip), string(atom)]),
/*user::http:encoding_filter(gzip, DataStream, GZipDataStream),*/
user::load_html(stream(GZipDataStream), Terms, []),
user::close_any(CloseIt)
)
)
Infortunately, the recovery part of catch doesn't work.
Any suggestion, please ?
The user:: prefixes in the goals suggests that the code you posted is a fragment of Logtalk. If so, it's misusing Logtalk source code and creating a dependency on the SWI-Prolog autoloading mechanism. The code can be rewritten for clarity and resilience. Doing that and fixing the bug in it (library(zlib) must be loaded to make avaialble the http:encoding_filter/3 filter) results in the following solution:
:- use_module(library(http/http_open), []).
:- use_module(library(sgml), []).
:- use_module(library(iostream), []).
:- use_module(library(zlib), []).
:- object(html).
:- public(get_url/2).
% override ambiguous meta-predicate template
:- meta_predicate(sgml:load_html(*,*,*)).
get_url(URL, Terms) :-
catch(
setup_call_cleanup(
http:http_open(URL, DataStream, []),
sgml:load_html(stream(DataStream), Terms, []),
close(DataStream)
),
_,
setup_call_cleanup(
iostream:open_any(URL, read, DataStream, CloseIt, [string(atom)]),
sgml:load_html(stream(DataStream), Terms, []),
iostream:close_any(CloseIt)
)
).
:- end_object.
The setup_call_cleanup/3 calls ensure that the opened streams are closed in case of error.
Assuming the object above is saved in a html.lgt file, the following sample call shows it working for the URL you posted:
?- {html}.
...
% (0 warnings)
true.
?- html::get_url('http://releases.llvm.org/6.0.0/tools/clang/docs/ClangCommandLineReference.html', Terms).
Terms = [element(html, [xmlns='http://www.w3.org/1999/xhtml'], [element(head, [], [element(meta, ['http-equiv'='Content-Type', content='text/html; charset=utf-8'], []), element(title, [], ['Clang command line argument reference — Clang 6 documentation']), element(link, [... = ...|...], []), element(link, [...|...], []), element(..., ..., ...)|...]), element(body, [role=document], [' ', element(div, [... = ...|...], [element(..., ..., ...)|...]), '\n ', element(..., ..., ...)|...])])].
I have an Ejabberd 17.01 installation where I need to push a notification in case a recipient is offline. This seems the be a common task and solutions using a customized Ejabberd module can be found everywhere. However, I just don't get it running. First, here's me script:
-module(mod_offline_push).
-behaviour(gen_mod).
-export([start/2, stop/1]).
-export([push_message/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
start(Host, _Opts) ->
?INFO_MSG("mod_offline_push loading", []),
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_message, 10),
ok.
stop(Host) ->
?INFO_MSG("mod_offline_push stopping", []),
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_message, 10),
ok.
push_message(From, To, Packet) ->
?INFO_MSG("mod_offline_push -> push_message", [To]),
Type = fxml:get_tag_attr_s(<<"type">>, Packet), % Supposedly since 16.04
%Type = xml:get_tag_attr_s(<<"type">>, Packet), % Supposedly since 13.XX
%Type = xml:get_tag_attr_s("type", Packet),
%Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
?INFO_MSG("mod_offline_push -> push_message", []),
ok.
The problem is the line Type = ... line in method push_message; without that line the last info message is logged (so the hook definitely works). When browsing online, I can find all kinds of function calls to extract elements from Packet. As far as I understand it changed over time with new releases. But it's not good, all variants lead in some kind of error. The current way returns:
2017-01-25 20:38:08.701 [error] <0.21678.0>#ejabberd_hooks:run1:332 {function_clause,[{fxml,get_tag_attr_s,[<<"type">>,{message,<<>>,normal,<<>>,{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>},[],[{text,<<>>,<<"sfsdfsdf">>}],undefined,[],#{}}],[{file,"src/fxml.erl"},{line,169}]},{mod_offline_push,push_message,3,[{file,"mod_offline_push.erl"},{line,33}]},{ejabberd_hooks,safe_apply,3,[{file,"src/ejabberd_hooks.erl"},{line,382}]},{ejabberd_hooks,run1,3,[{file,"src/ejabberd_hooks.erl"},{line,329}]},{ejabberd_sm,route,3,[{file,"src/ejabberd_sm.erl"},{line,126}]},{ejabberd_local,route,3,[{file,"src/ejabberd_local.erl"},{line,110}]},{ejabberd_router,route,3,[{file,"src/ejabberd_router.erl"},{line,87}]},{ejabberd_c2s,check_privacy_route,5,[{file,"src/ejabberd_c2s.erl"},{line,1886}]}]}
running hook: {offline_message_hook,[{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>},{message,<<>>,normal,<<>>,{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>},[],[{text,<<>>,<<"sfsdfsdf">>}],undefined,[],#{}}]}
I'm new Ejabberd and Erlang, so I cannot really interpret the error, but the Line 33 as mentioned in {mod_offline_push,push_message,3,[{file,"mod_offline_push.erl"}, {line,33}]} is definitely the line calling get_tag_attr_s.
UPDATE 2017/01/27: Since this cost me a lot of headache -- and I'm still not perfectly happy -- I post here my current working module in the hopes it might help others. My setup is Ejabberd 17.01 running on Ubuntu 16.04. Most stuff I tried and failed with seem to for older versions of Ejabberd:
-module(mod_fcm_fork).
-behaviour(gen_mod).
%% public methods for this module
-export([start/2, stop/1]).
-export([push_notification/3]).
%% included for writing to ejabberd log file
-include("ejabberd.hrl").
-include("logger.hrl").
-include("xmpp_codec.hrl").
%% Copied this record definition from jlib.hrl
%% Including "xmpp_codec.hrl" and "jlib.hrl" resulted in errors ("XYZ already defined")
-record(jid, {user = <<"">> :: binary(),
server = <<"">> :: binary(),
resource = <<"">> :: binary(),
luser = <<"">> :: binary(),
lserver = <<"">> :: binary(),
lresource = <<"">> :: binary()}).
start(Host, _Opts) ->
?INFO_MSG("mod_fcm_fork loading", []),
% Providing the most basic API to the clients and servers that are part of the Inets application
inets:start(),
% Add hook to handle message to user who are offline
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_notification, 10),
ok.
stop(Host) ->
?INFO_MSG("mod_fcm_fork stopping", []),
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, push_notification, 10),
ok.
push_notification(From, To, Packet) ->
% Generate JID of sender and receiver
FromJid = lists:concat([binary_to_list(From#jid.user), "#", binary_to_list(From#jid.server), "/", binary_to_list(From#jid.resource)]),
ToJid = lists:concat([binary_to_list(To#jid.user), "#", binary_to_list(To#jid.server), "/", binary_to_list(To#jid.resource)]),
% Get message body
MessageBody = Packet#message.body,
% Check of MessageBody is not empty
case MessageBody/=[] of
true ->
% Get first element (no idea when this list can have more elements)
[First | _ ] = MessageBody,
% Get message data and convert to string
MessageBodyText = binary_to_list(First#text.data),
send_post_request(FromJid, ToJid, MessageBodyText);
false ->
?INFO_MSG("mod_fcm_fork -> push_notification: MessageBody is empty",[])
end,
ok.
send_post_request(FromJid, ToJid, MessageBodyText) ->
%?INFO_MSG("mod_fcm_fork -> send_post_request -> MessageBodyText = ~p", [Demo]),
Method = post,
PostURL = gen_mod:get_module_opt(global, ?MODULE, post_url,fun(X) -> X end, all),
% Add data as query string. Not nice, query body would be preferable
% Problem: message body itself can be in a JSON string, and I couldn't figure out the correct encoding.
URL = lists:concat([binary_to_list(PostURL), "?", "fromjid=", FromJid,"&tojid=", ToJid,"&body=", edoc_lib:escape_uri(MessageBodyText)]),
Header = [],
ContentType = "application/json",
Body = [],
?INFO_MSG("mod_fcm_fork -> send_post_request -> URL = ~p", [URL]),
% ADD SSL CONFIG BELOW!
%HTTPOptions = [{ssl,[{versions, ['tlsv1.2']}]}],
HTTPOptions = [],
Options = [],
httpc:request(Method, {URL, Header, ContentType, Body}, HTTPOptions, Options),
ok.
Actually it fails with second arg Packet you pass to fxml:get_tag_attr_s in push_message function
{message,<<>>,normal,<<>>,
{jid,<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>,
<<"homer">>,<<"xxx.xxx.xxx.xxx">>,<<"conference">>},
{jid,<<"carl">>,<<"xxx.xxx.xxx.xxx">>,<<>>,<<"carl">>,
<<"xxx.xxx.xxx.xxx">>,<<>>},
[],
[{text,<<>>,<<"sfsdfsdf">>}],
undefined,[],#{}}
because it is not xmlel
Looks like it is record "message" defined in tools/xmpp_codec.hrl
with <<>> id and type 'normal'
xmpp_codec.hrl
-record(message, {id :: binary(),
type = normal :: 'chat' | 'error' | 'groupchat' | 'headline' | 'normal',
lang :: binary(),
from :: any(),
to :: any(),
subject = [] :: [#text{}],
body = [] :: [#text{}],
thread :: binary(),
error :: #error{},
sub_els = [] :: [any()]}).
Include this file and use just
Type = Packet#message.type
or, if you expect binary value
Type = erlang:atom_to_binary(Packet#message.type, utf8)
The newest way to do that seems to be with xmpp:get_type/1:
Type = xmpp:get_type(Packet),
It returns an atom, in this case normal.
I'm using asterisk/freepbx. Asterisk is not recording calls that are fetched from park. Here is my call flow.
A calls to Asterisk Server(AS)
Call is picked up by extension B
B does an attended transfer by dialling *2200 (200 is my default parking lot)
C dials 1 to fetch the parked call
C dials *1 to record the call.
Recording is not done.
I found a fix and it works for me. In the asterisk log i found that asterisk tries to record to an invalid file with no filename, just an extension(.wav). It executed 2 files - /var/lib/asterisk/agi-bin/parkfetch.agi and /var/lib/asterisk/bin/one_touch_record.php.
one_touch_record.php generates filename from data read from channel, like year, date, mixmonitor folder etc, but as there was no valid filename in the log, these should be null here.
$mixMonDir = getVariable($channel, "MIXMON_DIR");
$year = getVariable($channel, "YEAR");
$month = getVariable($channel, "MONTH");
$day = getVariable($channel, "DAY");
$mixMonFormat = getVariable($channel, "MIXMON_FORMAT");
$mixMonPost = getVariable($channel, "MIXMON_POST");
$astman->mixmonitor($channel, "{$mixMonDir}{$year}/{$month}/{$day}/{$callFileName}.{$mixMonFormat}", "a", $mixMonPost, rand());
So i inspected the parkfetch.agi were i found that these channel vars are copied only if REC_STATUS is "RECORDING" and in this case REC_STATUS is "INITIALIZED". So i added an OR clause ie i changed if ($rec_status == "RECORDING") to if ($rec_status == "RECORDING" || $rec_status=="INITIALIZED")
if ($channel) {
$rec_status = get_var("IMPORT($channel,REC_STATUS)");
$agi->set_variable('REC_STATUS', $rec_status);
if ($rec_status == "RECORDING" || $rec_status=="INITIALIZED") {
foreach (array('MIXMON_DIR', 'YEAR', 'MONTH', 'DAY', 'CALLFILENAME', 'MIXMON_FORMAT', 'MIXMON_POST', 'MON_FMT') as $v) {
$agi->set_variable($v, get_var("IMPORT($channel,$v)"));
}
}
}
And it worked. Now when I pressed *1 after fetching call from park, it is getting recorded.
If someone found a better solution, please leave it as a comment in my blog.
http://sachindotg.blogspot.in/2014/02/asterisk-cannot-record-call-after.html