I'm a user of HATENA-bookmark web service. It's a major social-bookmark service in Japan.
The API of the service is in public. So, I've written some code in Erlang.
To initiate operation, WSSE certification is required.
For details of WSSE specification, please refer
http://www.ibm.com/developerworks/webservices/library/specification/ws-secure/
For details of HATENA-bookmark API, please refer
http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d5%a5%a9%a5%c8%a5%e9%a5%a4%a5%d5AtomAPI?kid=88110#wsse (in Japanese)
Following is the wsse module.
-module(wsse).
-export([new/2]).
-define(SHA1DIGESTLENGTH, 20).
new (User, Password) ->
{A, B, C} = now(),
random:seed(A, B, C),
Nonce = nonce(?SHA1DIGESTLENGTH, ""),
%% create ISO 8601 compling datetime
%% $ ruby -e " require 'open-uri' ; p Time.now.iso8601"
%% => "2008-07-31T16:16:14+09:00"
%%
%% ref) http://www.trapexit.org/Converting_Between_struct:time_and_ISO8601_Format
{{Year, Month, Day}, {Hour, Min, Sec}} = erlang:localtime(),
TZ = os:cmd("date +%:z"),
Created = io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B~6s",
[Year, Month, Day, Hour, Min, Sec, TZ]),
crypto:start(),
Digest = binary_to_list(crypto:sha(Nonce ++ Created ++ Password)),
"UsernameToken Username=\"" ++ User ++ "\", " ++
"PasswordDigest=\"" ++
base64:encode_to_string(Digest) ++ "\", " ++
"Nonce=\"" ++
base64:encode_to_string(Nonce) ++ "\", " ++
"Created=\"" ++ Created ++ "\"".
nonce(0,L) -> L ++ [random:uniform(255)];
nonce(N,L) -> nonce(N -1, L ++ [random:uniform(255)]).
The HATENA-bookmark service API is using Atom format data.
Here is the module for it.
-module(getatom).
-export([new/3]).
new(User, Password, Uri) ->
RequestHeader = [{ "X-WSSE" , wsse:new(User, Password)}],
inets:start(),
{ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} =
http:request(get, {Uri, RequestHeader}, [ ], [ ]),
Body.
Here is a typical useage from erlang interpreter command line.
> Latest = getatom:new("username", "password", "http://b.hatena.ne.jp/atom/feed").
[60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,
46,48,34,32,101,110,99,111,100,105,110,103,61|...]
> io:format("~s",[Latest]).
<feed version="0.3"</pp>
xmlns="http://purl.org/atom/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
xml:lang="ja">
<title>kgbu\343\201\256\343\203\226\343\203\203\343\202\257\343\203\236\343\203\274\343\202\257</title>
<link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/kgbu/" />
...to be continued
In original article, endpoint to dump "latest" bookmarks only, to dump all bookmarks try below.
ReplyDeleteResults are stored in the file named "hatebudump.dat".
>Latest = getatom:new("username", "password", "http://b.hatena.ne.jp/dump").
[60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,
46,48,34,32,101,110,99,111,100,105,110,103,61|...]
> {ok,FH} = file:open("hatebudump.dat", write).
{ok,<0.78.0>}
> io:format(FH, "~s", [Latest]).