diff options
Diffstat (limited to 'src/web/pshb_http.erl')
-rw-r--r-- | src/web/pshb_http.erl | 792 |
1 files changed, 437 insertions, 355 deletions
diff --git a/src/web/pshb_http.erl b/src/web/pshb_http.erl index 02eb0a4cc..6f1a7a52c 100644 --- a/src/web/pshb_http.erl +++ b/src/web/pshb_http.erl @@ -25,7 +25,7 @@ %%%---------------------------------------------------------------------- %%% %%% {5280, ejabberd_http, [ -%%% http_poll, +%%% http_poll, %%% web_admin, %%% {request_handlers, [{["pshb"], pshb_http}]} % this should be added %%% ]} @@ -34,383 +34,465 @@ %%% curl -u cstar@localhost:encore -i -X POST http://localhost:5280/pshb/localhost/foo -d @sam.atom %%% +-module(pshb_http). --module (pshb_http). -author('ecestari@process-one.net'). --compile({no_auto_import,[error/1]}). +-compile({no_auto_import, [{error, 1}]}). -include("ejabberd.hrl"). + -include("jlib.hrl"). + -include("ejabberd_http.hrl"). -include("mod_pubsub/pubsub.hrl"). --export([process/2]). - -process([Domain | _Rest] = LocalPath, #request{auth = Auth} = Request)-> - UD = get_auth(Auth), - Module = backend(Domain), - case catch out(Module, Request, Request#request.method, LocalPath,UD) of - {'EXIT', Error} -> - ?ERROR_MSG("Error while processing ~p : ~n~p", [LocalPath, Error]), - error(500); - Result -> - Result - end. - +-export([process/2]). + +process([Domain | _Rest] = LocalPath, + #request{auth = Auth} = Request) -> + UD = get_auth(Auth), + Module = backend(Domain), + case catch out(Module, Request, Request#request.method, + LocalPath, UD) + of + {'EXIT', Error} -> + ?ERROR_MSG("Error while processing ~p : ~n~p", + [LocalPath, Error]), + error(500); + Result -> Result + end. + get_auth(Auth) -> case Auth of - {SJID, P} -> - case jlib:string_to_jid(SJID) of - error -> - undefined; - #jid{user = U, server = S} -> - case ejabberd_auth:check_password(U, S, P) of - true -> - {U, S}; - false -> - undefined - end - end; - _ -> - undefined + {SJID, P} -> + case jlib:string_to_jid(SJID) of + error -> undefined; + #jid{user = U, server = S} -> + case ejabberd_auth:check_password(U, S, P) of + true -> {U, S}; + false -> undefined + end + end; + _ -> undefined end. -out(Module, Args, 'GET', [Domain,Node]=Uri, _User) -> - case Module:tree_action(get_host(Uri), get_node, [get_host(Uri),get_collection(Uri)]) of - {error, Error} -> - error(Error); - #pubsub_node{options = Options}-> +out(Module, Args, 'GET', [Domain, Node] = Uri, _User) -> + case Module:tree_action(get_host(Uri), get_node, + [get_host(Uri), get_collection(Uri)]) + of + {error, Error} -> error(Error); + #pubsub_node{options = Options} -> AccessModel = lists:keyfind(access_model, 1, Options), case AccessModel of {access_model, open} -> - Items = lists:sort(fun(X,Y)-> - {DateX, _} = X#pubsub_item.modification, - {DateY, _} = Y#pubsub_item.modification, - DateX > DateY - end, Module:get_items( - get_host(Uri), - get_collection(Uri))), - case Items of - [] -> ?DEBUG("Items : ~p ~n", [collection(get_collection(Uri), - collection_uri(Args,Domain,Node), calendar:now_to_universal_time(erlang:now()), "", [])]), - {200, [{"Content-Type", "application/atom+xml"}], - collection(get_collection(Uri), - collection_uri(Args,Domain,Node), calendar:now_to_universal_time(erlang:now()), "", [])}; - _ -> - #pubsub_item{modification = {LastDate, _JID}} = LastItem = hd(Items), - Etag =generate_etag(LastItem), - IfNoneMatch=proplists:get_value('If-None-Match', Args#request.headers), - if IfNoneMatch==Etag - -> - success(304); - true -> - XMLEntries= [item_to_entry(Args,Domain, Node,Entry)||Entry <- Items], - {200, [{"Content-Type", "application/atom+xml"},{"Etag", Etag}], - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" - ++ xml:element_to_string( - collection(get_collection(Uri), collection_uri(Args,Domain,Node), - calendar:now_to_universal_time(LastDate), "", XMLEntries))} - end - end; - {access_model, Access} -> - ?INFO_MSG("Uri ~p requested. access_model is ~p. HTTP access denied unless access_model =:= open", - [Uri, Access]), - error(?ERR_FORBIDDEN) - end - end; - -out(Module, Args, 'POST', [_D, _Node]=Uri, {_User, _Domain} = UD) -> - publish_item(Module, Args, Uri, uniqid(false), UD); - -out(Module, Args, 'PUT', [_D, _Node, Slug]=Uri, {_User, _Domain} = UD) -> - publish_item(Module, Args, Uri, Slug, UD); - -out(Module, _Args, 'DELETE', [_D, Node, Id]= Uri, {User, UDomain}) -> - Jid = jlib:make_jid({User, UDomain, ""}), - case Module:delete_item(get_host(Uri), list_to_binary(Node), Jid, Id) of - {error, Error} -> error(Error); - {result, _Res} -> success(200) - end; - - -out(Module, Args, 'PUT', [_Domain, Node]= Uri, {User, UDomain}) -> - Host = get_host(Uri), - Jid = jlib:make_jid({User, UDomain, ""}), - Payload = xml_stream:parse_element(Args#request.data), - ConfigureElement = case xml:get_subtag(Payload, "configure") of - false ->[]; - {xmlelement, _, _, SubEls}->SubEls - end, - case Module:set_configure(Host, list_to_binary(Node), Jid, ConfigureElement, Args#request.lang) of - {result, []} -> success(200); - {error, Error} -> error(Error) - end; - -out(Module, Args, 'GET', [Domain]=Uri, From)-> - Host = get_host(Uri), - ?DEBUG("Host = ~p", [Host]), - case Module:tree_action(Host, get_subnodes, [Host, <<>>, From ]) of - [] -> - ?DEBUG("Error getting URI ~p : ~p",[Uri, From]), - error(?ERR_ITEM_NOT_FOUND); - Collections -> - {200, [{"Content-Type", "application/atomsvc+xml"}], "<?xml version=\"1.0\" encoding=\"utf-8\"?>" - ++ xml:element_to_string(service(Args,Domain, Collections))} - end; - -out(Module, Args, 'POST', [Domain]=Uri, {User, UDomain})-> - Host = get_host(Uri), - Payload = xml_stream:parse_element(Args#request.data), - {Node, Type} = case xml:get_subtag(Payload, "create") of - false -> {<<>>,"flat"}; - E -> - {list_to_binary(get_tag_attr_or_default("node", E,"")), - get_tag_attr_or_default("type", E,"flat")} - end, - ConfigureElement = case xml:get_subtag(Payload, "configure") of - false ->[]; - {xmlelement, _, _, SubEls}->SubEls - end, - Jid = jlib:make_jid({User, UDomain, ""}), - case Module:create_node(Host, Domain, Node, Jid, Type, all, ConfigureElement) of - {error, Error} -> - ?ERROR_MSG("Error create node via HTTP : ~p",[Error]), - error(Error); % will probably detail more - {result, [Result]} -> - {200, [{"Content-Type", "application/xml"}], "<?xml version=\"1.0\" encoding=\"utf-8\"?>" - ++ xml:element_to_string(Result)} - end; - -out(Module,_Args, 'DELETE', [_Domain, Node] = Uri, {User, UDomain})-> - Host = get_host(Uri), - Jid = jlib:make_jid({User, UDomain, ""}), - BinNode = list_to_binary(Node), - case Module:delete_node(Host, BinNode, Jid) of - {error, Error} -> error(Error); - {result, []} -> - {200, [],[]} - end; - - - -out(Module, Args, 'GET', [Domain, Node, _Item]=URI, _) -> - Failure = fun(Error)-> - ?DEBUG("Error getting URI ~p : ~p",[URI, Error]), - error(Error) - end, - Success = fun(Item)-> - Etag =generate_etag(Item), - IfNoneMatch=proplists:get_value('If-None-Match', Args#request.headers), - if IfNoneMatch==Etag - -> - success(304); - true -> - {200, [{"Content-Type", "application/atom+xml"},{"Etag", Etag}], "<?xml version=\"1.0\" encoding=\"utf-8\"?>" - ++ xml:element_to_string(item_to_entry(Args, Domain,Node, Item))} - end - end, - get_item(Module, URI, Failure, Success); - -out(_Module,_,Method,Uri,undefined) -> - ?DEBUG("Error, ~p not authorized for ~p : ~p",[ Method,Uri]), - error(?ERR_FORBIDDEN). - -get_item(Module, Uri, Failure, Success)-> - ?DEBUG(" Module:get_item(~p, ~p,~p)", [get_host(Uri), get_collection(Uri), get_member(Uri)]), - case Module:get_item(get_host(Uri), get_collection(Uri), get_member(Uri)) of - {error, Reason} -> - Failure(Reason); - #pubsub_item{}=Item -> - Success(Item) - end. - -publish_item(Module, Args, [Domain, Node | _R] = Uri, Slug, {User, Domain})-> - - Payload = xml_stream:parse_element(Args#request.data), - [FilteredPayload]=xml:remove_cdata([Payload]), - - %FilteredPayload2 = case xml:get_subtag(FilteredPayload, "app:edited") -> - % {xmlelement, Name, Attrs, [{cdata, }]} - case Module:publish_item(get_host(Uri), - Domain, - get_collection(Uri), - jlib:make_jid(User,Domain, ""), - Slug, - [FilteredPayload]) of - {result, [_]} -> - ?DEBUG("Publishing to ~p~n",[entry_uri(Args, Domain, Node,Slug)]), - {201, [{"location", entry_uri(Args, Domain,Node,Slug)}], Payload}; - {error, Error} -> - error(Error) - end. - -generate_etag(#pubsub_item{modification={{_, D2, D3}, _JID}})->integer_to_list(D3+D2). -get_host([Domain|_Rest])-> "pubsub."++Domain. -get_collection([_Domain, Node|_Rest])->list_to_binary(Node). -get_member([_Domain, _Node, Member])-> - Member. + Items = lists:sort(fun (X, Y) -> + {DateX, _} = + X#pubsub_item.modification, + {DateY, _} = + Y#pubsub_item.modification, + DateX > DateY + end, + Module:get_items(get_host(Uri), + get_collection(Uri))), + case Items of + [] -> + ?DEBUG("Items : ~p ~n", + [collection(get_collection(Uri), + collection_uri(Args, Domain, Node), + calendar:now_to_universal_time(erlang:now()), + <<"">>, [])]), + {200, + [{<<"Content-Type">>, <<"application/atom+xml">>}], + collection(get_collection(Uri), + collection_uri(Args, Domain, Node), + calendar:now_to_universal_time(erlang:now()), + <<"">>, [])}; + _ -> + #pubsub_item{modification = {LastDate, _JID}} = + LastItem = hd(Items), + Etag = generate_etag(LastItem), + IfNoneMatch = proplists:get_value('If-None-Match', + Args#request.headers), + if IfNoneMatch == Etag -> success(304); + true -> + XMLEntries = [item_to_entry(Args, Domain, Node, + Entry) + || Entry <- Items], + {200, + [{<<"Content-Type">>, <<"application/atom+xml">>}, + {<<"Etag">>, Etag}], + <<"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", + (xml:element_to_binary(collection(get_collection(Uri), + collection_uri(Args, + Domain, + Node), + calendar:now_to_universal_time(LastDate), + <<"">>, + XMLEntries)))/binary>>} + end + end; + {access_model, Access} -> + ?INFO_MSG("Uri ~p requested. access_model is ~p. " + "HTTP access denied unless access_model " + "=:= open", + [Uri, Access]), + error(?ERR_FORBIDDEN) + end + end; +out(Module, Args, 'POST', [_D, _Node] = Uri, + {_User, _Domain} = UD) -> + publish_item(Module, Args, Uri, uniqid(false), UD); +out(Module, Args, 'PUT', [_D, _Node, Slug] = Uri, + {_User, _Domain} = UD) -> + publish_item(Module, Args, Uri, Slug, UD); +out(Module, _Args, 'DELETE', [_D, Node, Id] = Uri, + {User, UDomain}) -> + Jid = jlib:make_jid({User, UDomain, <<"">>}), + case Module:delete_item(get_host(Uri), + iolist_to_binary(Node), Jid, Id) + of + {error, Error} -> error(Error); + {result, _Res} -> success(200) + end; +out(Module, Args, 'PUT', [_Domain, Node] = Uri, + {User, UDomain}) -> + Host = get_host(Uri), + Jid = jlib:make_jid({User, UDomain, <<"">>}), + Payload = xml_stream:parse_element(Args#request.data), + ConfigureElement = case xml:get_subtag(Payload, + <<"configure">>) + of + false -> []; + #xmlel{children = SubEls} -> SubEls + end, + case Module:set_configure(Host, iolist_to_binary(Node), + Jid, ConfigureElement, Args#request.lang) + of + {result, []} -> success(200); + {error, Error} -> error(Error) + end; +out(Module, Args, 'GET', [Domain] = Uri, From) -> + Host = get_host(Uri), + ?DEBUG("Host = ~p", [Host]), + case Module:tree_action(Host, get_subnodes, + [Host, <<>>, From]) + of + [] -> + ?DEBUG("Error getting URI ~p : ~p", [Uri, From]), + error(?ERR_ITEM_NOT_FOUND); + Collections -> + {200, + [{<<"Content-Type">>, <<"application/atomsvc+xml">>}], + <<"<?xml version=\"1.0\" encoding=\"utf-8\"?>", + (xml:element_to_binary(service(Args, Domain, + Collections)))/binary>>} + end; +out(Module, Args, 'POST', [Domain] = Uri, + {User, UDomain}) -> + Host = get_host(Uri), + Payload = xml_stream:parse_element(Args#request.data), + {Node, Type} = case xml:get_subtag(Payload, + <<"create">>) + of + false -> {<<>>, <<"flat">>}; + E -> + {get_tag_attr_or_default(<<"node">>, E, <<"">>), + get_tag_attr_or_default(<<"type">>, E, <<"flat">>)} + end, + ConfigureElement = case xml:get_subtag(Payload, + <<"configure">>) + of + false -> []; + #xmlel{children = SubEls} -> SubEls + end, + Jid = jlib:make_jid({User, UDomain, <<"">>}), + case Module:create_node(Host, Domain, Node, Jid, Type, + all, ConfigureElement) + of + {error, Error} -> + ?ERROR_MSG("Error create node via HTTP : ~p", [Error]), + error(Error); + {result, [Result]} -> + {200, [{<<"Content-Type">>, <<"application/xml">>}], + <<"<?xml version=\"1.0\" encoding=\"utf-8\"?>", + (xml:element_to_binary(Result))/binary>>} + end; +out(Module, _Args, 'DELETE', [_Domain, Node] = Uri, + {User, UDomain}) -> + Host = get_host(Uri), + Jid = jlib:make_jid({User, UDomain, <<"">>}), + case Module:delete_node(Host, Node, Jid) of + {error, Error} -> error(Error); + {result, []} -> {200, [], []} + end; +out(Module, Args, 'GET', [Domain, Node, _Item] = URI, + _) -> + Failure = fun (Error) -> + ?DEBUG("Error getting URI ~p : ~p", [URI, Error]), + error(Error) + end, + Success = fun (Item) -> + Etag = generate_etag(Item), + IfNoneMatch = proplists:get_value('If-None-Match', + Args#request.headers), + if IfNoneMatch == Etag -> success(304); + true -> + {200, + [{<<"Content-Type">>, <<"application/atom+xml">>}, + {<<"Etag">>, Etag}], + <<"<?xml version=\"1.0\" encoding=\"utf-8\"?>", + (xml:element_to_binary(item_to_entry(Args, + Domain, + Node, + Item)))/binary>>} + end + end, + get_item(Module, URI, Failure, Success); +out(_Module, _, Method, Uri, undefined) -> + ?DEBUG("Error, ~p not authorized for ~p : ~p", + [Method, Uri]), + error(?ERR_FORBIDDEN). + +get_item(Module, Uri, Failure, Success) -> + ?DEBUG(" Module:get_item(~p, ~p,~p)", + [get_host(Uri), get_collection(Uri), get_member(Uri)]), + case Module:get_item(get_host(Uri), get_collection(Uri), + get_member(Uri)) + of + {error, Reason} -> Failure(Reason); + #pubsub_item{} = Item -> Success(Item) + end. + +publish_item(Module, Args, [Domain, Node | _R] = Uri, + Slug, {User, Domain}) -> + Payload = xml_stream:parse_element(Args#request.data), + [FilteredPayload] = xml:remove_cdata([Payload]), + case Module:publish_item(get_host(Uri), Domain, + get_collection(Uri), + jlib:make_jid(User, Domain, <<"">>), Slug, + [FilteredPayload]) + of + {result, [_]} -> + ?DEBUG("Publishing to ~p~n", + [entry_uri(Args, Domain, Node, Slug)]), + {201, + [{<<"location">>, entry_uri(Args, Domain, Node, Slug)}], + Payload}; + {error, Error} -> error(Error) + end. + +generate_etag(#pubsub_item{modification = + {{_, D2, D3}, _JID}}) -> + jlib:integer_to_binary(D3 + D2). + +get_host([Domain | _Rest]) -> + <<"pubsub.", Domain/binary>>. + +get_collection([_Domain, Node | _Rest]) -> + Node. + +get_member([_Domain, _Node, Member]) -> Member. collection_uri(R, Domain, Node) -> - base_uri(R, Domain)++ "/" ++ b2l(Node). - -entry_uri(R,Domain, Node, Id)-> - collection_uri(R,Domain, Node)++"/"++b2l(Id). - -base_uri(#request{host=Host, port=Port}, Domain)-> - "http://"++Host++":"++i2l(Port)++"/pshb/" ++ Domain. - -item_to_entry(Args,Domain, Node,#pubsub_item{itemid={Id,_}, payload=Entry}=Item)-> - [R]=xml:remove_cdata(Entry), - item_to_entry(Args, Domain, Node, Id, R, Item). - -item_to_entry(Args,Domain, Node, Id,{xmlelement, "entry", Attrs, SubEl}, - #pubsub_item{modification={ Secs, JID} }) -> - Date = calendar:now_to_local_time(Secs), - {_User, Domain, _}=jlib:jid_tolower(JID), - SubEl2=[{xmlelement, "app:edited", [], [{xmlcdata, w3cdtf(Date)}]}, - {xmlelement, "updated", [],[{xmlcdata, w3cdtf(Date)}]}, - {xmlelement, "author", [],[{xmlelement, "name", [], [{xmlcdata, list_to_binary(jlib:jid_to_string(JID))}]}]}, - {xmlelement, "link",[{"rel", "edit"}, - {"href", entry_uri(Args,Domain,Node, Id)}],[] }, - {xmlelement, "id", [],[{xmlcdata, entry_uri(Args, Domain, Node, Id)}]} - | SubEl], - {xmlelement, "entry", [{"xmlns:app","http://www.w3.org/2007/app"}|Attrs], SubEl2}; - + <<(base_uri(R, Domain))/binary, "/", + Node/binary>>. + +entry_uri(R, Domain, Node, Id) -> + <<(collection_uri(R, Domain, Node))/binary, "/", + Id/binary>>. + +base_uri(#request{host = Host, port = Port}, Domain) -> + <<"http://", Host/binary, ":", (i2l(Port))/binary, + "/pshb/", Domain/binary>>. + +item_to_entry(Args, Domain, Node, + #pubsub_item{itemid = {Id, _}, payload = Entry} = + Item) -> + [R] = xml:remove_cdata(Entry), + item_to_entry(Args, Domain, Node, Id, R, Item). + +item_to_entry(Args, Domain, Node, Id, + #xmlel{name = <<"entry">>, attrs = Attrs, + children = SubEl}, + #pubsub_item{modification = {Secs, JID}}) -> + Date = calendar:now_to_local_time(Secs), + {_User, Domain, _} = jlib:jid_tolower(JID), + SubEl2 = [#xmlel{name = <<"app:edited">>, attrs = [], + children = [{xmlcdata, w3cdtf(Date)}]}, + #xmlel{name = <<"updated">>, attrs = [], + children = [{xmlcdata, w3cdtf(Date)}]}, + #xmlel{name = <<"author">>, attrs = [], + children = + [#xmlel{name = <<"name">>, attrs = [], + children = + [{xmlcdata, + jlib:jid_to_string(JID)}]}]}, + #xmlel{name = <<"link">>, + attrs = + [{<<"rel">>, <<"edit">>}, + {<<"href">>, entry_uri(Args, Domain, Node, Id)}], + children = []}, + #xmlel{name = <<"id">>, attrs = [], + children = + [{xmlcdata, entry_uri(Args, Domain, Node, Id)}]} + | SubEl], + #xmlel{name = <<"entry">>, + attrs = + [{<<"xmlns:app">>, <<"http://www.w3.org/2007/app">>} + | Attrs], + children = SubEl2}; % Don't do anything except adding xmlns -item_to_entry(_Args,_Domain, Node, _Id, {xmlelement, Name, Attrs, Subels}=Element, _Item)-> - case proplists:is_defined("xmlns",Attrs) of - true -> Element; - false -> {xmlelement, Name, [{"xmlns", Node}|Attrs], Subels} - end. - -collection(Title, Link, Updated, _Id, Entries)-> - {xmlelement, "feed", [{"xmlns", "http://www.w3.org/2005/Atom"}, - {"xmlns:app", "http://www.w3.org/2007/app"}], [ - {xmlelement, "title", [],[{xmlcdata, Title}]}, - {xmlelement, "generator", [],[{xmlcdata, <<"ejabberd">>}]}, - {xmlelement, "updated", [],[{xmlcdata, w3cdtf(Updated)}]}, - {xmlelement, "link", [{"href", Link}, {"rel", "self"}], []}, - {xmlelement, "id", [], [{xmlcdata, list_to_binary(Link)}]}, - {xmlelement, "title", [],[{xmlcdata, Title}]} | - Entries - ]}. - -service(Args, Domain,Collections)-> - {xmlelement, "service", [{"xmlns", "http://www.w3.org/2007/app"}, - {"xmlns:atom", "http://www.w3.org/2005/Atom"}, - {"xmlns:app", "http://www.w3.org/2007/app"}],[ - {xmlelement, "workspace", [],[ - {xmlelement, "atom:title", [],[{xmlcdata,"Pubsub node Feed for " ++Domain}]} | - lists:map(fun(#pubsub_node{nodeid={_Server, Id}, type=_Type})-> - {xmlelement, "collection", [{"href", collection_uri(Args,Domain, Id)}], [ - {xmlelement, "atom:title", [], [{xmlcdata, Id}]} - ]} - end, Collections) - ]} - ]}. - -%% simple output functions -error({xmlelement, "error", Attrs, _}=Error) -> - Value = list_to_integer(xml:get_attr_s("code", Attrs)), - {Value, [{"Content-type", "application/xml"}], xml:element_to_string(Error)}; -error(404)-> - {404, [], "Not Found"}; -error(403)-> - {403, [], "Forbidden"}; -error(500)-> - {500, [], "Internal server error"}; -error(401)-> - {401, [{"WWW-Authenticate", "basic realm=\"ejabberd\""}],"Unauthorized"}; -error(Code)-> - {Code, [], ""}. -success(200)-> - {200, [], ""}; -success(Code)-> - {Code, [], ""}. - -backend(Domain)-> - Modules = gen_mod:loaded_modules(Domain), - case lists:member(mod_pubsub_odbc, Modules) of - true -> mod_pubsub_odbc; - _ -> mod_pubsub - end. - - -% Code below is taken (with some modifications) from the yaws webserver, which -% is distributed under the folowing license: -% -% This software (the yaws webserver) is free software. -% Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org> -% Any use or misuse of the source code is hereby freely allowed. -% -% 1. Redistributions of source code must retain the above copyright -% notice as well as this list of conditions. -% -% 2. Redistributions in binary form must reproduce the above copyright -% notice as well as this list of conditions. -%%% Create W3CDTF (http://www.w3.org/TR/NOTE-datetime) formatted date -%%% w3cdtf(GregSecs) -> "YYYY-MM-DDThh:mm:ssTZD" -%%% -uniqid(false)-> - {T1, T2, T3} = now(), - lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])); -uniqid(Slug) -> - Slut = string:to_lower(Slug), - S = string:substr(Slut, 1, 9), - {_T1, T2, T3} = now(), - lists:flatten(io_lib:fwrite("~s-~.16B~.16B", [S, T2, T3])). - -w3cdtf(Date) -> %1 Date = calendar:gregorian_seconds_to_datetime(GregSecs), - {{Y, Mo, D},{H, Mi, S}} = Date, - [UDate|_] = calendar:local_time_to_universal_time_dst(Date), - {DiffD,{DiffH,DiffMi,_}}=calendar:time_difference(UDate,Date), - w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi). - -%%% w3cdtf's helper function -w3cdtf_diff(Y, Mo, D, H, Mi, S, _DiffD, DiffH, DiffMi) when DiffH < 12, DiffH /= 0 -> - i2l(Y) ++ "-" ++ add_zero(Mo) ++ "-" ++ add_zero(D) ++ "T" ++ - add_zero(H) ++ ":" ++ add_zero(Mi) ++ ":" ++ - add_zero(S) ++ "+" ++ add_zero(DiffH) ++ ":" ++ add_zero(DiffMi); - -w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi) when DiffH > 12, DiffD == 0 -> - i2l(Y) ++ "-" ++ add_zero(Mo) ++ "-" ++ add_zero(D) ++ "T" ++ - add_zero(H) ++ ":" ++ add_zero(Mi) ++ ":" ++ - add_zero(S) ++ "+" ++ add_zero(DiffH) ++ ":" ++ - add_zero(DiffMi); - -w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi) when DiffH > 12, DiffD /= 0, DiffMi /= 0 -> - i2l(Y) ++ "-" ++ add_zero(Mo) ++ "-" ++ add_zero(D) ++ "T" ++ - add_zero(H) ++ ":" ++ add_zero(Mi) ++ ":" ++ - add_zero(S) ++ "-" ++ add_zero(23-DiffH) ++ - ":" ++ add_zero(60-DiffMi); - -w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi) when DiffH > 12, DiffD /= 0, DiffMi == 0 -> - i2l(Y) ++ "-" ++ add_zero(Mo) ++ "-" ++ add_zero(D) ++ "T" ++ - add_zero(H) ++ ":" ++ add_zero(Mi) ++ ":" ++ - add_zero(S) ++ "-" ++ add_zero(24-DiffH) ++ - ":" ++ add_zero(DiffMi); - -w3cdtf_diff(Y, Mo, D, H, Mi, S, _DiffD, DiffH, _DiffMi) when DiffH == 0 -> - i2l(Y) ++ "-" ++ add_zero(Mo) ++ "-" ++ add_zero(D) ++ "T" ++ - add_zero(H) ++ ":" ++ add_zero(Mi) ++ ":" ++ - add_zero(S) ++ "Z". +item_to_entry(_Args, _Domain, Node, _Id, + #xmlel{name = Name, attrs = Attrs, children = Subels} = + Element, + _Item) -> + case proplists:is_defined(<<"xmlns">>, Attrs) of + true -> Element; + false -> + #xmlel{name = Name, + attrs = [{<<"xmlns">>, Node} | Attrs], + children = Subels} + end. -add_zero(I) when is_integer(I) -> add_zero(i2l(I)); -add_zero([A]) -> [$0,A]; -add_zero(L) when is_list(L) -> L. +collection(Title, Link, Updated, _Id, Entries) -> + #xmlel{name = <<"feed">>, + attrs = + [{<<"xmlns">>, <<"http://www.w3.org/2005/Atom">>}, + {<<"xmlns:app">>, <<"http://www.w3.org/2007/app">>}], + children = + [#xmlel{name = <<"title">>, attrs = [], + children = [{xmlcdata, Title}]}, + #xmlel{name = <<"generator">>, attrs = [], + children = [{xmlcdata, <<"ejabberd">>}]}, + #xmlel{name = <<"updated">>, attrs = [], + children = [{xmlcdata, w3cdtf(Updated)}]}, + #xmlel{name = <<"link">>, + attrs = [{<<"href">>, Link}, {<<"rel">>, <<"self">>}], + children = []}, + #xmlel{name = <<"id">>, attrs = [], + children = [{xmlcdata, iolist_to_binary(Link)}]}, + #xmlel{name = <<"title">>, attrs = [], + children = [{xmlcdata, Title}]} + | Entries]}. + +service(Args, Domain, Collections) -> + #xmlel{name = <<"service">>, + attrs = + [{<<"xmlns">>, <<"http://www.w3.org/2007/app">>}, + {<<"xmlns:atom">>, <<"http://www.w3.org/2005/Atom">>}, + {<<"xmlns:app">>, <<"http://www.w3.org/2007/app">>}], + children = + [#xmlel{name = <<"workspace">>, attrs = [], + children = + [#xmlel{name = <<"atom:title">>, attrs = [], + children = + [{xmlcdata, + <<"Pubsub node Feed for ", + Domain/binary>>}]} + | lists:map(fun (#pubsub_node{nodeid = + {_Server, Id}, + type = _Type}) -> + #xmlel{name = <<"collection">>, + attrs = + [{<<"href">>, + collection_uri(Args, + Domain, + Id)}], + children = + [#xmlel{name = + <<"atom:title">>, + attrs = [], + children = + [{xmlcdata, + Id}]}]} + end, + Collections)]}]}. + +error(#xmlel{name = <<"error">>, attrs = Attrs} = + Error) -> + Value = + jlib:binary_to_integer(xml:get_attr_s(<<"code">>, + Attrs)), + {Value, [{<<"Content-type">>, <<"application/xml">>}], + xml:element_to_binary(Error)}; +error(404) -> {404, [], <<"Not Found">>}; +error(403) -> {403, [], <<"Forbidden">>}; +error(500) -> {500, [], <<"Internal server error">>}; +error(401) -> + {401, + [{<<"WWW-Authenticate">>, + <<"basic realm=\"ejabberd\"">>}], + <<"Unauthorized">>}; +error(Code) -> {Code, [], <<"">>}. + +success(200) -> {200, [], <<"">>}; +success(Code) -> {Code, [], <<"">>}. + +backend(Domain) -> + Modules = gen_mod:loaded_modules(Domain), + case lists:member(mod_pubsub_odbc, Modules) of + true -> mod_pubsub_odbc; + _ -> mod_pubsub + end. +uniqid(false) -> + {T1, T2, T3} = now(), + list_to_binary(io_lib:fwrite("~.16B~.16B~.16B", + [T1, T2, T3])). +w3cdtf(Date) -> %1 Date = calendar:gregorian_seconds_to_datetime(GregSecs), + {{Y, Mo, D}, {H, Mi, S}} = Date, + [UDate | _] = + calendar:local_time_to_universal_time_dst(Date), + {DiffD, {DiffH, DiffMi, _}} = + calendar:time_difference(UDate, Date), + w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi). + +w3cdtf_diff(Y, Mo, D, H, Mi, S, _DiffD, DiffH, DiffMi) + when DiffH < 12, DiffH /= 0 -> + <<(i2l(Y))/binary, "-", (add_zero(Mo))/binary, "-", + (add_zero(D))/binary, "T", (add_zero(H))/binary, ":", + (add_zero(Mi))/binary, ":", (add_zero(S))/binary, "+", + (add_zero(DiffH))/binary, ":", + (add_zero(DiffMi))/binary>>; +w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi) + when DiffH > 12, DiffD == 0 -> + <<(i2l(Y))/binary, "-", (add_zero(Mo))/binary, "-", + (add_zero(D))/binary, "T", (add_zero(H))/binary, ":", + (add_zero(Mi))/binary, ":", (add_zero(S))/binary, "+", + (add_zero(DiffH))/binary, ":", + (add_zero(DiffMi))/binary>>; +w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi) + when DiffH > 12, DiffD /= 0, DiffMi /= 0 -> + <<(i2l(Y))/binary, "-", (add_zero(Mo))/binary, "-", + (add_zero(D))/binary, "T", (add_zero(H))/binary, ":", + (add_zero(Mi))/binary, ":", (add_zero(S))/binary, "-", + (add_zero(23 - DiffH))/binary, ":", + (add_zero(60 - DiffMi))/binary>>; +w3cdtf_diff(Y, Mo, D, H, Mi, S, DiffD, DiffH, DiffMi) + when DiffH > 12, DiffD /= 0, DiffMi == 0 -> + <<(i2l(Y))/binary, "-", (add_zero(Mo))/binary, "-", + (add_zero(D))/binary, "T", (add_zero(H))/binary, ":", + (add_zero(Mi))/binary, ":", (add_zero(S))/binary, "-", + (add_zero(24 - DiffH))/binary, ":", + (add_zero(DiffMi))/binary>>; +w3cdtf_diff(Y, Mo, D, H, Mi, S, _DiffD, DiffH, _DiffMi) + when DiffH == 0 -> + <<(i2l(Y))/binary, "-", (add_zero(Mo))/binary, "-", + (add_zero(D))/binary, "T", (add_zero(H))/binary, ":", + (add_zero(Mi))/binary, ":", (add_zero(S))/binary, "Z">>. -i2l(I) when is_integer(I) -> integer_to_list(I); -i2l(L) when is_list(L) -> L. +add_zero(I) when is_integer(I) -> add_zero(i2l(I)); +add_zero(<<A>>) -> <<$0, A>>; +add_zero(L) when is_binary(L) -> L. -b2l(B) when is_binary(B) -> binary_to_list(B); -b2l(L) when is_list(L) -> L. +i2l(I) when is_integer(I) -> + jlib:integer_to_binary(I). -get_tag_attr_or_default(AttrName, Element, Default)-> - case xml:get_tag_attr_s(AttrName, Element) of - "" -> Default; - Val -> Val - end. +get_tag_attr_or_default(AttrName, Element, Default) -> + case xml:get_tag_attr_s(AttrName, Element) of + <<"">> -> Default; + Val -> Val + end. |