Related
i got this code that take webhook message but i dont understand the pattern matching behind it
function getContent(link)
cmd = io.popen('powershell -command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Write-Host (Invoke-WebRequest -Uri "'..link..'").Content"')
return cmd:read("*all"):match('"description": "(.+)"}],')
end
function blabla(a)
link= webhookLink.."/messages/"..messageId
text = [[
$webHookUrl = "]]..link..[["
$embedObject = #{
description = "]]..getContent(link):gsub("\\([nt])", {n="\n", t="\t"})..[[`n]]..a..[["
}
$embedArray = #($embedObject)
$payload = #{
embeds = $embedArray
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-RestMethod -Uri $webHookUrl -Body ($payload | ConvertTo-Json -Depth 4) -Method Patch -ContentType 'application/json'
]]
file = io.popen("powershell -command -", "w")
file:write(text)
file:close()
end
My question is what does "(.+)"}] mean in the getContent function and ]]..getContent(link):gsub("\\([nt])", {n="\n", t="\t"})..[[n]]..a..[[inblabla` function ?
https://gitspartv.github.io/lua-patterns/
This can help you greatly with pattern matching,
though, (.+) means capture any digit, with multiple repetations.
example: "ABC(.+)" would return everything after "ABC"
return cmd:read("*all"):match('"description": "(.+)"}],')
Looks like this gets everything inside description: "THIS_IS_RETURNED"],
Let's break up the pattern: "description": "(.+)"}],
"description": " - literal string that needs to match
(.+) - one or more of any character, greedy (+), captured
"}], - another literal string
That is, this pattern will match the first substring that starts with "description": ", followed by one or more characters which are captured, followed by "}],.
All in all, this pattern is a very unreliable implementation of extracting a certain string from what's probably JSON; you should use a proper JSON parser instead. This pattern will fail in all of the following cases:
Empty String: [[{"description": ""}],null] (might be intended)
Another String later on: [{"foo": [{"description": "bar"}], "baz": "world"}], null], which would match as bar"}], "baz": "world due to + doing greedy matching.
. means any character, and + means one or more. I suggest you to read up on the basics of patterns. For example: http://lua-users.org/wiki/PatternsTutorial
I am new to elixir and I need to create ISO 8583 client with this language and Phoenix framework. I found an Erlang library for it from stackoverflow thread here, compiled successfully and followed the example in the repository here but got error when marshaling the message. Here is my Elixir code to marshal the message:
msg1 = :erl8583_message.new()
msg2 = :erl8583_message.set_mti("0800", msg1)
msg3 = :erl8583_message.set(3, "300000", msg2)
msg4 = :erl8583_message.set(24, "045", msg3)
msg5 = :erl8583_message.set(41, "11111111", msg4)
msg6 = :erl8583_message.set(42, "222222222222222", msg5)
msg7 = :erl8583_message.set(63, "This is a Test Message", msg6)
marshalled = :erl8583_marshaller_ascii.marshal(msg7)
That's just an elixir version from the example on the repo. This is the error I've got when running the app:
[error] #PID<0.438.0> running TestlangIsoClientWeb.Endpoint (cowboy_protocol) terminated
Server: 127.0.0.1:4001 (http)
Request: POST /api/process
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in :erl8583_marshaller_ascii.marshal_data_element/2
(erl8583) /home/muhammad/Workspace/testlang/testlang_iso_client/deps/erl8583/src/erl8583_marshaller_ascii.erl:168: :erl8583_marshaller_ascii.marshal_data_element({:n, :fixed, 4}, "0800")
(erl8583) /home/muhammad/Workspace/testlang/testlang_iso_client/deps/erl8583/src/erl8583_marshaller.erl:108: :erl8583_marshaller.marshal/2
(testlang_iso_client) lib/testlang_iso_client_web/controllers/my_controller.ex:61: TestlangIsoClientWeb.MyController.process/2
(testlang_iso_client) lib/testlang_iso_client_web/controllers/my_controller.ex:1: TestlangIsoClientWeb.MyController.action/2
(testlang_iso_client) lib/testlang_iso_client_web/controllers/my_controller.ex:1: TestlangIsoClientWeb.MyController.phoenix_controller_pipeline/2
(testlang_iso_client) lib/testlang_iso_client_web/endpoint.ex:1: TestlangIsoClientWeb.Endpoint.instrument/4
(phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
(testlang_iso_client) lib/testlang_iso_client_web/endpoint.ex:1: TestlangIsoClientWeb.Endpoint.plug_builder_call/2
(testlang_iso_client) lib/testlang_iso_client_web/endpoint.ex:1: TestlangIsoClientWeb.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /home/muhammad/Workspace/testlang/testlang_iso_client/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
Is there something I missed to make it work? Any help would be very appreciated.
Updated
I have tried to changed the string parameter to charlist, but still got the same error. Here is the code snippet:
def test(conn, _params) do
IO.puts("Test")
msg1 = :erl8583_message.new()
msg2 = :erl8583_message.set_mti('0800', msg1)
msg3 = :erl8583_message.set(3, '300000', msg2)
msg4 = :erl8583_message.set(24, '045', msg3)
msg5 = :erl8583_message.set(41, '11111111', msg4)
msg6 = :erl8583_message.set(42, '222222222222222', msg5)
msg7 = :erl8583_message.set(63, 'This is a Test Message', msg6)
marshalled = :erl8583_marshaller_ascii.marshal(msg7)
json(conn, %{status: "ok"})
end
Here is the function erl8583_marshaller.erl:108 mentioned in the stacktrace:
marshal(Message, MarshalHandlers) ->
OptionsRecord = parse_options(MarshalHandlers, #marshal_options{}),
{Marshalled1, Message1} = init_marshalling(OptionsRecord, Message),
MarshalledMti = encode_mti(OptionsRecord, Message1), % --- Line 108
Marshalled2 = <<Marshalled1/binary, MarshalledMti/binary>>,
{MarshalledBitmap, Message2} = encode_bitmap(OptionsRecord, Message1),
Marshalled3 = <<Marshalled2/binary, MarshalledBitmap/binary>>,
MarshalledFields = encode_fields(OptionsRecord, Message2),
Marshalled4 = <<Marshalled3/binary, MarshalledFields/binary>>,
end_marshalling(OptionsRecord, Message2, Marshalled4).
And here is the function erl8583_marshaller_ascii.erl:168 mentioned in the stacktrace:
%%
%% Local Functions
%%
marshal_data_element({n, llvar, Length}, FieldValue) when length(FieldValue) =< Length ->
erl8583_convert:integer_to_string(length(FieldValue), 2) ++ FieldValue;
I don't understand why the call to that function was failed to match with parameters {:n, :fixed, 4}, "0800" that was sent from my function. I have tried to change the double quotes to single quotes with no success. Is there any other suggestions what am I supposed to do?
there may be a bug in the code.
you can reference this issue here
there is one elixir library out which is relatively new.
its called ale8583.
documentation is still coming out on this one but looks very promising.
you can check it out.
Try passing charlists instead of strings:
msg1 = :erl8583_message.new()
msg2 = :erl8583_message.set_mti('0800', msg1)
msg3 = :erl8583_message.set(3, '300000', msg2)
msg4 = :erl8583_message.set(24, '045', msg3)
msg5 = :erl8583_message.set(41, '11111111', msg4)
msg6 = :erl8583_message.set(42, '222222222222222', msg5)
msg7 = :erl8583_message.set(63, 'This is a Test Message', msg6)
marshalled = :erl8583_marshaller_ascii.marshal(msg7)
There is potential for confusion here:
What Erlang calls "strings" and puts in double quotes ("foo"), Elixir calls "charlists" and puts in single quotes ('foo').
What Elixir calls "strings" and puts in double quotes ("foo"), Erlang calls "binaries" and puts in double quotes plus angle brackets (<<"foo">>).
It seems like the erl8583 library expects Erlang strings throughout.
I have this concrete syntax:
syntax SomeMore = [...] SyncBlock? sync;
syntax SyncBlock = "sync" "{" SyncStatement* stats "}";
syntax SyncStatement = [...];
[SyncBlock]"sync { <syncStrings> }" seems to work, but when I try to use it as a SyncBlock? and assign it:
SyncBlock? sync = [SyncBlock?]"sync { <syncStrings> }"
it does not work: inline parsing not supported on SyncBlock?, what is the easiest way to build up a value of this X?-type?
Can I convert a SyncBlock to a SyncBlock? somehow?
Something like this also doesn’t work:
syncBlock = (SyncBlock?)`sync { <SyncStatement* syncs>}`;
P.S. SyncBlock? syncBlock = … results in Ambiguous code (internal error), SyncBlock? syncBlock = …. Probably due to a ternary operator ambiguity?
I found a workaround, not ideal, but it works.
It seems that the ? in the types introduces some difficulties, but can be circumvented using an "alias" for this type:
I changed the grammar to:
syntax SomeMore = [...] MaybeSyncBlock sync;
syntax MaybeSyncBlock = SyncBlock?;
syntax SyncBlock = "sync" "{" SyncStatement* stats "}";
syntax SyncStatement = [...];
Now this works:
MaybeSyncBlock syncBlock = [MaybeSyncBlock]"sync { <syncStrings> }";
This is my first time using erlang and I decided to try and write a wrapper for an API. Here's what I've got so far:-
-module(my_api_wrapper).
%% API exports
-export([auth/0]).
auth() ->
application:start(inets),
application:start(ssl),
AuthStr = base64:encode_to_string("username:password"),
Method = post,
URL = "https://api.endpoint.com/auth",
Header = [{"Authorization", "Basic " ++ AuthStr}],
Type = "application/json",
Body = "{\"grant_type\":\"client_credentials\"}",
HTTPOptions = [],
Options = [],
httpc:request(Method, {URL, Header, Type, Body}, HTTPOptions, Options).
When testing this at the shell I get an error:-
{error,{failed_connect,[{to_address,{"api.endpoint.com",
443}},
{inet,[inet],closed}]}}
I can't figure out what I'm doing wrong here! I'm running this version Erlang/OTP 19 [erts-8.0.2]. Any help appreciated.
For anyone who it might help - here's exactly what I changed to make the code in my original question work - thanks to Dogbert for his comment above.
-module(my_api_wrapper).
%% API exports
-export([auth/0]).
auth() ->
application:start(inets),
application:start(ssl),
AuthStr = base64:encode_to_string("username:password"),
Method = post,
URL = "https://api.endpoint.com/auth",
Header = [{"Authorization", "Basic " ++ AuthStr}],
Type = "application/json",
Body = "{\"grant_type\":\"client_credentials\"}",
% ADD SSL CONFIG BELOW!
HTTPOptions = [{ssl,[{versions, ['tlsv1.2']}]}],
Options = [],
httpc:request(Method, {URL, Header, Type, Body}, HTTPOptions, Options).
I am trying to extend this library https://github.com/tim/erlang-oauth-examples to allow the client to make multipart posts (as necessary for Twitter's statuses/update_with_media). So far the, this is my attempt at the extra final signing and authentication logic that is necessary. The formation of the Body, using Boundaries is done elsewhere, and I don't think is the stumbling block.
oauth_post({multipart, Boundary}, URL, Body, Consumer, Token, TokenSecret) ->
BodyH = base64:encode_to_string(crypto:hash(sha, Body)),
Signed = oauth:sign("POST", URL, [{"oauth_body_hash", BodyH}], Consumer, Token, TokenSecret),
{[[{ "oauth_signature", Sig}],
[{"oauth_body_hash", BBody}]], Rest} =
proplists:split(Signed, ["oauth_signature", "oauth_body_hash"]),
Encoded = [ {"oauth_signature", oauth:uri_encode(Sig)}
, {"oauth_body_hash", oauth:uri_encode(BBody)}
| Rest],
Sorted = lists:sort(Encoded),
Auth = lists:flatten(string:join([ K++"=\""++V++"\"" || {K,V} <- Sorted], ", ")),
OAuth = "OAuth " ++ Auth,
ContentType = "multipart/form-data;boundary=" ++ Boundary,
Headers = [ {"Authorization", OAuth}
, {"Content-Type", ContentType}
, {"Content-Length", integer_to_list(length(Body))}],
Request = {URL, Headers, ContentType, Body},
httpc:request(post, Request, [], []).
But so far this method call fails to Authenticate. Can anyone, with this domain expertise, see what I'm doing wrong? Many thanks.
After Answer
Per Paul's answer below, this is what I ended up using. Updated my fork of the library too.
oauth_post({multipart, Boundary}, URL, Body, Consumer, Token, TokenSecret) ->
BodyH = base64:encode_to_string(crypto:hash(sha, Body)),
Signed = oauth:sign("POST", URL, [{"oauth_body_hash", BodyH}]
, Consumer, Token, TokenSecret),
{AuthorizationParams, []} =
lists:partition(fun({K, _}) -> lists:prefix("oauth_", K) end, Signed),
Headers = [ oauth:header(AuthorizationParams)],
ContentType = "multipart/form-data;boundary=" ++ Boundary,
Request = {URL, Headers, ContentType, Body},
httpc:request(post, Request, [], []).
The issue is that your code only URI-encodes the signature and the body hash. All oauth_* parameters must be URI-encoded, and especially oauth_nonce which is a Base64 string in this OAuth library.
Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
As a side note:
Fetching the value for key "oauth_body_hash" does not make sense since you passed this value in the first place (it is in BodyH).
You don't need to sort the oauth_ parameters in the signature.
You don't need the Content-Type and Content-Length headers, inets' httpc will add them for you unless you pass headers_as_is option.
Simply do:
oauth_post({multipart, Boundary}, URL, Body, Consumer, Token, TokenSecret) ->
BodyH = base64:encode_to_string(crypto:hash(sha, Body)),
Signed = oauth:sign("POST", URL, [{"oauth_body_hash", BodyH}], Consumer, Token, TokenSecret),
% URI encode values returned by oauth:sign/6 for the Authorization header
Auth = lists:flatten(string:join([ K++"=\""++oauth:uri_encode(V)++"\"" || {K,V} <- Signed], ", ")),
OAuth = "OAuth " ++ Auth,
ContentType = "multipart/form-data;boundary=" ++ Boundary,
Headers = [ {"Authorization", OAuth} ],
Request = {URL, Headers, ContentType, Body},
httpc:request(post, Request, [], []).