Tuesday, July 31, 2007

Connecting Erlang to Blogger (Part 2) - Adding an entry

For this second part, we start where we left the last time.
We were able to read the response of a succesful login, data was three lines of key value pairs.
The last line holds the final 'AuthToken' we need to send to the blogger atom post service...

So here's the code to extract the line that begins with the 'Auth' keyword and store the value after the '=' and before the end of line:

extract_auth(<<>>) ->
{error, not_found};
extract_auth(<<"Error=", Rest/binary>>) ->
Size = size(Rest) - 1,
<<Msg:Size/binary, _/binary>> = Rest,
{error, binary_to_list(Msg)};

extract_auth(<<"Auth=", Rest/binary>>) ->
{ok, Rest};
extract_auth(<<_:1/binary, Rest/binary>>) ->
extract_auth(Rest).

Note that we are also able to read 'Error' lines, those lines are sent in case of an failed login attempt...
I can describe what 'extract_auth/1' do like this:
  1. if binary in empty returns {error, not_found}
  2. if binary begins with 'Error' catch it and returns its content with the tuple {error, Msg}
  3. if binary begins with the 'Auth' keyword extract everything till the end of the binary
  4. in any other case extract one character and parse the rest of the binary


To conclude, upon successful login this fun will return:

{ok, "AAAAAQAARAFA..."}

This is, of course our Blogger AuthToken...

Now how can we use it ? This is Simple !
Open a new file named blogger.erl and write something like this:

-module(blogger).
-export([ new/3, post/3, test/2, template/3 ]).

post(AuthToken, Title, Content) ->
Data = template(Title, Content),
request(AuthToken, iolist_to_binary(Data)).

template(Title, Content) when is_list(Content) ->
template(Title, Content, {"none", "none"}).

template(Title, Content, Author) when is_list(Content) ->
template(Title, list_to_binary(Content), Author);

template(Title, Content, Author) ->
{AuthorName, AuthorEmail} = Author,
[ <<"<entry xmlns=\"http://www.w3.org/2005/Atom\">\n<title type=\"text\">\n">>,
list_to_binary(Title),
% <<"</title><content type='xhtml'><div xmlns='http://www.w3.org/1999/xhtml'>">>,
<<"</title>\n<content type=\"text\">">>,
Content,
<<"</content>\n<author><name>">>,
list_to_binary(AuthorName),
<<"</name>">>,
list_to_binary(AuthorEmail),
<<"<
</author>\n</entry>\n">> ].

request(AuthToken, Data) when is_binary(AuthToken) ->
request(binary_to_list(AuthToken), Data);

request(AuthToken, Data) ->
io:format("Sending: ~nContent-length: ~p~nBody:~n~s~n", [ size(Data), Data ]),
Authorization = "GoogleLogin auth=" ++ AuthToken,
Url = "http://www.blogger.com/feeds/199963XXXX081936700/posts/default", % Put your BlogID, this one is invalid
case http:request(post,
{ Url,
[ { "Authorization", Authorization } ],
"application/atom+xml; charset=utf-8", Data},
[ {timeout, 3000}, {sync, false} ],
[ {body_format, binary} ]) of

{ok, Result} ->
%io:format("Received: ~p~n", [Result]),
{_,_,Body} = Result,
Body;

{error, Reason} ->
io:format("Error: ~p~n", [Reason])
end.


Once you're done, you'll be pleased to found that this doesn't work :/.
Yep, I wasn't able to post anything !
May be I've missed something in the documentation, but all I get is an nice SAXexception...

1 comment:

Ben said...

I've seen that "content is not allowed in prolog" error before... I'm pretty sure it's because there's something before the xml declaration.

Check the xml you're trying to parse carefully. The '<' of the xml declaration should be the very first character.

Sticky