diff --git a/sha_erl.c b/sha_erl.c new file mode 100644 index 000000000..bce02b762 --- /dev/null +++ b/sha_erl.c @@ -0,0 +1,271 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is SHA 180-1 Reference Implementation (Compact version) + * + * The Initial Developer of the Original Code is Paul Kocher of + * Cryptography Research. Portions created by Paul Kocher are + * Copyright (C) 1995-9 by Cryptography Research, Inc. All + * Rights Reserved. + * + * Contributor(s): + * + */ + +#include +#include +#include + +typedef struct { + ErlDrvPort port; +} sha_data; + +typedef struct { + unsigned long H[5]; + unsigned long W[80]; + int lenW; + unsigned long sizeHi,sizeLo; +} j_SHA_CTX; + +static void shaHashBlock(j_SHA_CTX *ctx); + +void shaInit(j_SHA_CTX *ctx) { + int i; + + ctx->lenW = 0; + ctx->sizeHi = ctx->sizeLo = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) + */ + ctx->H[0] = 0x67452301L; + ctx->H[1] = 0xefcdab89L; + ctx->H[2] = 0x98badcfeL; + ctx->H[3] = 0x10325476L; + ctx->H[4] = 0xc3d2e1f0L; + + for (i = 0; i < 80; i++) + ctx->W[i] = 0; +} + + +void shaUpdate(j_SHA_CTX *ctx, unsigned char *dataIn, int len) { + int i; + + /* Read the data into W and process blocks as they get full + */ + for (i = 0; i < len; i++) { + ctx->W[ctx->lenW / 4] <<= 8; + ctx->W[ctx->lenW / 4] |= (unsigned long)dataIn[i]; + if ((++ctx->lenW) % 64 == 0) { + shaHashBlock(ctx); + ctx->lenW = 0; + } + ctx->sizeLo += 8; + ctx->sizeHi += (ctx->sizeLo < 8); + } +} + + +void shaFinal(j_SHA_CTX *ctx, unsigned char hashout[20]) { + unsigned char pad0x80 = 0x80; + unsigned char pad0x00 = 0x00; + unsigned char padlen[8]; + int i; + + /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length + */ + padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255); + padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255); + padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255); + padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255); + padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255); + padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255); + padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255); + padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255); + shaUpdate(ctx, &pad0x80, 1); + while (ctx->lenW != 56) + shaUpdate(ctx, &pad0x00, 1); + shaUpdate(ctx, padlen, 8); + + /* Output hash + */ + for (i = 0; i < 20; i++) { + hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24); + ctx->H[i / 4] <<= 8; + } + + /* + * Re-initialize the context (also zeroizes contents) + */ + shaInit(ctx); +} + + +void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) { + j_SHA_CTX ctx; + + shaInit(&ctx); + shaUpdate(&ctx, dataIn, len); + shaFinal(&ctx, hashout); +} + + +#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL) + +static void shaHashBlock(j_SHA_CTX *ctx) { + int t; + unsigned long A,B,C,D,E,TEMP; + + for (t = 16; t <= 79; t++) + ctx->W[t] = + SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1); + + A = ctx->H[0]; + B = ctx->H[1]; + C = ctx->H[2]; + D = ctx->H[3]; + E = ctx->H[4]; + + for (t = 0; t <= 19; t++) { + TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + for (t = 20; t <= 39; t++) { + TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + for (t = 40; t <= 59; t++) { + TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + for (t = 60; t <= 79; t++) { + TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} + +/*---------------------------------------------------------------------------- + * + * This code added by Thomas "temas" Muldowney for Jabber compatability + * + *---------------------------------------------------------------------------*/ +char *shahash(char *str) +{ + static char final[41]; + char *pos; + unsigned char hashval[20]; + int x; + + if(!str || strlen(str) == 0) + return NULL; + + shaBlock((unsigned char *)str, strlen(str), hashval); + + pos = final; + for(x=0;x<20;x++) + { + snprintf(pos, 3, "%02x", hashval[x]); + pos += 2; + } + return (char *)final; +} + +void shahash_r(const char* str, char hashbuf[41]) +{ + int x; + char *pos; + unsigned char hashval[20]; + + if(!str || strlen(str) == 0) + return; + + shaBlock((unsigned char *)str, strlen(str), hashval); + + pos = hashbuf; + for(x=0;x<20;x++) + { + snprintf(pos, 3, "%02x", hashval[x]); + pos += 2; + } + + return; +} + +static ErlDrvData sha_erl_start(ErlDrvPort port, char *buff) +{ + sha_data* d = (sha_data*)driver_alloc(sizeof(sha_data)); + d->port = port; + + return (ErlDrvData)d; +} + +static void sha_erl_stop(ErlDrvData handle) +{ + driver_free((char*)handle); +} + +static void sha_erl_output(ErlDrvData handle, char *buff, int bufflen) +{ + sha_data* d = (sha_data*)handle; + ei_x_buff buf; + + static char final[41]; + char *pos; + unsigned char hashval[20]; + int x; + + if(bufflen == 0) + final[0] = '\000'; + else + { + shaBlock(buff, bufflen, hashval); + + pos = final; + for(x=0;x<20;x++) + { + snprintf(pos, 3, "%02x", hashval[x]); + pos += 2; + } + } + + ei_x_new_with_version(&buf); + ei_x_encode_string(&buf, final); + + driver_output(d->port, buf.buff, buf.index); + ei_x_free(&buf); +} + + +ErlDrvEntry sha_driver_entry = { + NULL, /* F_PTR init, N/A */ + sha_erl_start, /* L_PTR start, called when port is opened */ + sha_erl_stop, /* F_PTR stop, called when port is closed */ + sha_erl_output, /* F_PTR output, called when erlang has sent */ + NULL, /* F_PTR ready_input, called when input descriptor ready */ + NULL, /* F_PTR ready_output, called when output descriptor ready */ + "sha_erl", /* char *driver_name, the argument to open_port */ + NULL, /* F_PTR finish, called when unloaded */ + NULL, /* F_PTR control, port_command callback */ + NULL, /* F_PTR timeout, reserved */ + NULL /* F_PTR outputv, reserved */ +}; + +DRIVER_INIT(sha_erl) /* must match name in driver_entry */ +{ + return &sha_driver_entry; +} + diff --git a/src/Makefile b/src/Makefile index ff57225b0..12ff0b40d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,14 +5,16 @@ INCLUDES = -I/usr/lib/erlang/usr/include \ LIBDIRS = -L/usr/lib/erlang/lib/erl_interface-3.3.0/lib -all: expat_erl.so +ERLSHLIBS = expat_erl.so sha_erl.so + +all: $(ERLSHLIBS) erl -make -expat_erl.so: expat_erl.c - gcc -Wall $(INCLUDES) $(LIBDIRS) \ - -lexpat \ - expat_erl.c \ - -lerl_interface \ - -lei \ - -o expat_erl.so -fpic -shared \ +$(ERLSHLIBS): %.so: %.c + gcc -Wall $(INCLUDES) $(LIBDIRS) \ + -lexpat \ + $(subst .so,.c,$@) \ + -lerl_interface \ + -lei \ + -o $@ -fpic -shared \ diff --git a/src/ejabberd.erl b/src/ejabberd.erl index e313cec82..e560d79cf 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -21,6 +21,7 @@ init() -> ok = erl_ddll:load_driver(".", expat_erl), Port = open_port({spawn, expat_erl}, [binary]), db_init(), + sha:start(), ejabberd_auth:start(), ejabberd_router:start(), ejabberd_sm:start(), diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 4463932c0..86b6d3b49 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -19,6 +19,7 @@ -export([start/0, start_link/0, set_password/2, check_password/2, + check_password/4, try_register/2]). %% gen_server callbacks @@ -110,6 +111,31 @@ check_password(User, Password) -> false end. +check_password(User, Password, StreamID, Digest) -> + LUser = jlib:tolower(User), + F = fun() -> + case mnesia:read({passwd, LUser}) of + [E] -> + E#passwd.password + end + end, + case mnesia:transaction(F) of + {atomic, Passwd} -> + DigRes = if + Digest /= "" -> + Digest == sha:sha(StreamID ++ Passwd); + true -> + false + end, + if DigRes -> + true; + true -> + Passwd == Password + end; + _ -> + false + end. + set_password(User, Password) -> LUser = jlib:tolower(User), diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 60946e4be..ddce27c5e 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -113,9 +113,9 @@ wait_for_auth({xmlstreamelement, El}, StateData) -> {auth, ID, {U, P, D, R}} -> io:format("AUTH: ~p~n", [{U, P, D, R}]), % TODO: digested password - case ejabberd_auth:check_password(U, P) of + case ejabberd_auth:check_password(U, P, + StateData#state.streamid, D) of true -> - % TODO ejabberd_sm:open_session(U, R), Res = jlib:make_result_iq_reply(El), send_element(StateData#state.sender, Res), @@ -181,7 +181,9 @@ session_established({xmlstreamelement, El}, StateData) -> [FromJID, El, StateData]), presence_update(FromJID, El, StateData); _ -> - ejabberd_router:route(FromJID, ToJID, El), + ejabberd_router:route({StateData#state.user, + Server, ""}, + ToJID, El), presence_track(FromJID, ToJID, El, StateData) end; _ -> @@ -267,24 +269,12 @@ handle_info({route, From, To, Packet}, StateName, StateData) -> Attrs1 = lists:keydelete("type", 1, Attrs), {true, [{"type", "unavailable"} | Attrs1], StateData}; "subscribe" -> - mod_roster:in_subscription(StateData#state.user, - {FU, FS, ""}, - subscribe), {true, Attrs, StateData}; "subscribed" -> - mod_roster:in_subscription(StateData#state.user, - {FU, FS, ""}, - subscribed), {true, Attrs, StateData}; "unsubscribe" -> - mod_roster:in_subscription(StateData#state.user, - {FU, FS, ""}, - unsubscribe), {true, Attrs, StateData}; "unsubscribed" -> - mod_roster:in_subscription(StateData#state.user, - {FU, FS, ""}, - unsubscribed), {true, Attrs, StateData}; _ -> {true, Attrs, StateData} @@ -450,10 +440,17 @@ presence_update(From, Packet, StateData) -> true -> StateData end, - StateData; "error" -> StateData; + "subscribe" -> + StateData; + "subscribed" -> + StateData; + "unsubscribe" -> + StateData; + "unsubscribed" -> + StateData; _ -> FromUnavail = (StateData#state.pres_last == undefined) or StateData#state.pres_invis, @@ -479,28 +476,36 @@ presence_update(From, Packet, StateData) -> presence_track(From, To, Packet, StateData) -> {xmlelement, Name, Attrs, Els} = Packet, + LTo = jlib:jid_tolower(To), + User = StateData#state.user, case xml:get_attr_s("type", Attrs) of "unavailable" -> - I = remove_element(To, StateData#state.pres_i), - A = remove_element(To, StateData#state.pres_a), + I = remove_element(LTo, StateData#state.pres_i), + A = remove_element(LTo, StateData#state.pres_a), StateData#state{pres_i = I, pres_a = A}; "invisible" -> - I = ?SETS:add_element(To, StateData#state.pres_i), - A = remove_element(To, StateData#state.pres_a), + I = ?SETS:add_element(LTo, StateData#state.pres_i), + A = remove_element(LTo, StateData#state.pres_a), StateData#state{pres_i = I, pres_a = A}; "subscribe" -> + mod_roster:out_subscription(User, To, subscribe), StateData; "subscribed" -> + mod_roster:out_subscription(User, To, subscribed), StateData; "unsubscribe" -> + mod_roster:out_subscription(User, To, unsubscribe), StateData; "unsubscribed" -> + mod_roster:out_subscription(User, To, unsubscribed), + StateData; + "error" -> StateData; _ -> - I = remove_element(To, StateData#state.pres_i), - A = ?SETS:add_element(To, StateData#state.pres_a), + I = remove_element(LTo, StateData#state.pres_i), + A = ?SETS:add_element(LTo, StateData#state.pres_a), StateData#state{pres_i = I, pres_a = A} end. diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index b6657effd..e28eeb741 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -100,17 +100,7 @@ do_route(From, To, Packet) -> end, case mnesia:transaction(F) of {atomic, error} -> - % TODO: start s2s instead of below ejabberd_s2s ! {route, From, To, Packet}; - %{xmlelement, Name, Attrs, SubTags} = Packet, - %case xml:get_attr_s("type", Attrs) of - % "error" -> - % ok; - % _ -> - % Err = jlib:make_error_reply(Packet, - % "502", "Service Unavailable"), - % ejabberd_router ! {route, To, From, Err} - %end; {atomic, {ok, Node, Pid}} -> case node() of Node -> diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index ca45cbbca..c032e24fa 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -186,26 +186,49 @@ wait_for_verification(closed, StateData) -> stream_established({xmlstreamelement, El}, StateData) -> {xmlelement, Name, Attrs, Els} = El, - % TODO - From = xml:get_attr_s("from", Attrs), - FromJID1 = jlib:string_to_jid(From), - FromJID = case FromJID1 of - {Node, Server, Resource} -> - if Server == StateData#state.server -> FromJID1; - true -> error - end; - _ -> error - end, - To = xml:get_attr_s("to", Attrs), - ToJID = case To of - "" -> error; - _ -> jlib:string_to_jid(To) - end, - if ((Name == "iq") or (Name == "message") or (Name == "presence")) and - (ToJID /= error) and (FromJID /= error) -> - ejabberd_router:route(FromJID, ToJID, El); - true -> - error + case Name of + "db:verify" -> + case is_key_packet(El) of + {verify, To, From, Id, Key} -> + io:format("VERIFY KEY: ~p~n", [{To, From, Id, Key}]), + Key1 = ejabberd_s2s:get_key(From), + Type = if Key == Key1 -> "valid"; + true -> "invalid" + end, + send_element(StateData#state.socket, + {xmlelement, + "db:verify", + [{"from", ?MYNAME}, + {"to", From}, + {"id", Id}, + {"type", Type}], + []}); + _ -> + ok + end; + _ -> + From = xml:get_attr_s("from", Attrs), + FromJID1 = jlib:string_to_jid(From), + FromJID = case FromJID1 of + {Node, Server, Resource} -> + if Server == StateData#state.server -> FromJID1; + true -> error + end; + _ -> error + end, + To = xml:get_attr_s("to", Attrs), + ToJID = case To of + "" -> error; + _ -> jlib:string_to_jid(To) + end, + if ((Name == "iq") or + (Name == "message") or + (Name == "presence")) and + (ToJID /= error) and (FromJID /= error) -> + ejabberd_router:route(FromJID, ToJID, El); + true -> + error + end end, {next_state, stream_established, StateData}; diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 975be95fe..ea767a39d 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -169,18 +169,45 @@ do_route(From, To, Packet) -> % TODO case Name of "presence" -> - lists:foreach( - fun(R) -> - if From /= {User, Server, R} -> - ejabberd_sm ! {route, - From, - {User, Server, R}, - Packet}; - true -> - ok - end - end, get_user_resources(User)), - ok; + {FU, FS, FR} = From, + Pass = case xml:get_attr_s("type", Attrs) of + "subscribe" -> + mod_roster:in_subscription(User, + {FU, FS, ""}, + subscribe); + "subscribed" -> + mod_roster:in_subscription(User, + {FU, FS, ""}, + subscribed); + "unsubscribe" -> + mod_roster:in_subscription(User, + {FU, FS, ""}, + unsubscribe); + "unsubscribed" -> + mod_roster:in_subscription(User, + {FU, FS, ""}, + unsubscribed); + _ -> + true + end, + if Pass -> + LFrom = jlib:jid_tolower(From), + LUser = jlib:tolower(User), + LServer = jlib:tolower(Server), + lists:foreach( + fun(R) -> + if LFrom /= {LUser, LServer, R} -> + ejabberd_sm ! {route, + From, + {User, Server, R}, + Packet}; + true -> + ok + end + end, get_user_resources(User)); + true -> + ok + end; "message" -> % TODO ok; diff --git a/src/jlib.erl b/src/jlib.erl index 62910a129..e450b80bb 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -20,6 +20,7 @@ string_to_jid/1, jid_to_string/1, tolower/1, + jid_tolower/1, get_iq_namespace/1, iq_query_info/1, is_iq_request_type/1, @@ -179,6 +180,8 @@ tolower(S) -> lists:map(fun tolower_c/1, S). +jid_tolower({U, S, R}) -> + {tolower(U), tolower(S), R}. get_iq_namespace({xmlelement, Name, Attrs, Els}) when Name == "iq" -> case xml:remove_cdata(Els) of diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 197dc29eb..6c23a0f14 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -15,15 +15,18 @@ -export([start/0, init/0, process_iq/3, get_subscription_lists/1, - in_subscription/3]). + in_subscription/3, + out_subscription/3]). -include_lib("mnemosyne/include/mnemosyne.hrl"). -include("ejabberd.hrl"). --record(roster, {user, +-record(roster, {uj, + user, jid, name = "", subscription = none, + ask = none, groups = [], xattrs = [], xs = []}). @@ -35,9 +38,8 @@ start() -> init() -> mnesia:create_table(roster,[{disc_copies, [node()]}, - {type, bag}, {attributes, record_info(fields, roster)}]), - mnesia:add_table_index(roster, jid), + mnesia:add_table_index(roster, user), ejabberd_local:register_iq_handler("jabber:iq:roster", ?MODULE, process_iq), loop(). @@ -83,7 +85,7 @@ process_iq_get(From, To, {iq, ID, Type, XMLNS, SubEl}) -> {User, _, _} = From, LUser = jlib:tolower(User), F = fun() -> - mnesia:read({roster, LUser}) + mnesia:index_read(roster, LUser, #roster.user) end, case mnesia:transaction(F) of {atomic, Items} -> @@ -108,15 +110,24 @@ item_to_xml(Item) -> Attrs3 = case Item#roster.subscription of none -> [{"subscription", "none"} | Attrs2]; + from -> + [{"subscription", "from"} | Attrs2]; + to -> + [{"subscription", "to"} | Attrs2]; both -> [{"subscription", "both"} | Attrs2]; remove -> - [{"subscription", "remove"} | Attrs2]; - _ -> - % TODO - Attrs2 + [{"subscription", "remove"} | Attrs2] end, - Attrs = Attrs3 ++ Item#roster.xattrs, + Attrs4 = case Item#roster.ask of + none -> + Attrs3; + subscribe -> + [{"ask", "subscribe"} | Attrs3]; + unsubscribe -> + [{"ask", "unsubscribe"} | Attrs3] + end, + Attrs = Attrs4 ++ Item#roster.xattrs, SubEls1 = lists:map(fun(G) -> {xmlelement, "group", [], [{xmlcdata, G}]} end, Item#roster.groups), @@ -139,21 +150,18 @@ process_item_set(User, To, XItem) -> error -> ok; _ -> + LJID = jlib:jid_tolower(JID), F = fun() -> - Res = mnemosyne:eval(query [X || X <- table(roster), - X.user = LUser, - X.jid = JID] - end), + Res = mnesia:read({roster, {LUser, LJID}}), Item = case Res of [] -> - #roster{user = LUser, - jid = JID, - groups = [], - xattrs = [], - xs = []}; + #roster{uj = {LUser, LJID}, + user = LUser, + jid = JID}; [I] -> - mnesia:delete_object(I), - I#roster{name = "", + I#roster{user = LUser, + jid = JID, + name = "", groups = [], xattrs = [], xs = []} @@ -162,7 +170,7 @@ process_item_set(User, To, XItem) -> Item2 = process_item_els(Item1, Els), case Item2#roster.subscription of remove -> - ok; + mnesia:delete({roster, {LUser, LJID}}); _ -> mnesia:write(Item2) end, @@ -249,7 +257,7 @@ push_item(User, Resource, From, Item) -> get_subscription_lists(User) -> LUser = jlib:tolower(User), F = fun() -> - mnesia:read({roster, LUser}) + mnesia:index_read(roster, LUser, #roster.user) end, case mnesia:transaction(F) of {atomic, Items} -> @@ -259,7 +267,8 @@ get_subscription_lists(User) -> end. fill_subscription_lists([I | Is], F, T) -> - J = I#roster.jid, + %J = I#roster.jid, + J = element(2, I#roster.uj), case I#roster.subscription of both -> fill_subscription_lists(Is, [J | F], [J | T]); @@ -276,14 +285,146 @@ fill_subscription_lists([], F, T) -> in_subscription(User, From, Type) -> LUser = jlib:tolower(User), + LFrom = jlib:jid_tolower(From), + {FU, FS, FR} = From, F = fun() -> - mnesia:read({roster, LUser}) + case mnesia:read({roster, {LUser, LFrom}}) of + [] -> + case Type of + subscribe -> + true; + unsubscribe -> + true; + unsubscribed -> + false; + subscribed -> + NewItem = #roster{uj = {LUser, LFrom}, + user = LUser, + jid = From}, + mnesia:write(NewItem), + true + end; + [I] -> + case Type of + subscribe -> + S = I#roster.subscription, + if + (S == both) or (S == from) -> + ejabberd_router:route( + {User, ?MYNAME, ""}, {FU, FS, ""}, + {xmlelement, "presence", + [{"type", "subscribed"}], []}), + % TODO: update presence + false; + true -> + true + end; + unsubscribe -> + S = I#roster.subscription, + if + (S == none) or (S == to) -> + ejabberd_router:route( + {User, ?MYNAME, ""}, {FU, FS, ""}, + {xmlelement, "presence", + [{"type", "unsubscribed"}], []}), + % TODO: update presence + false; + true -> + true + end; + _ -> + S = I#roster.subscription, + NS = case Type of + subscribed -> + case S of + from -> both; + none -> to; + _ -> S + end; + unsubscribed -> + case S of + both -> from; + to -> none; + _ -> S + end + end, + NewItem = I#roster{subscription = NS, + ask = none}, + mnesia:write(NewItem), + {push, NewItem} + end + end end, case mnesia:transaction(F) of - {atomic, Items} -> - % TODO - ok; + {atomic, true} -> + true; + {atomic, false} -> + false; + {atomic, {push, Item}} -> + push_item(User, {"", ?MYNAME, ""}, Item), + true; _ -> - ok + false + end. + +out_subscription(User, JID, Type) -> + LUser = jlib:tolower(User), + LJID = jlib:jid_tolower(JID), + F = fun() -> + Item = case mnesia:read({roster, {LUser, LJID}}) of + [] -> + if (Type == unsubscribe) or + (Type == unsubscribed) -> + false; + true -> + #roster{uj = {LUser, LJID}, + user = LUser, + jid = JID} + end; + [I] -> + I + end, + if Item == false -> + ok; + true -> + NewItem = + case Type of + subscribe -> + Item#roster{ask = subscribe}; + unsubscribe -> + Item#roster{ask = unsubscribe}; + subscribed -> + S = Item#roster.subscription, + NS = case S of + to -> both; + none -> from; + _ -> S + end, + % TODO: update presence + Item#roster{subscription = NS, + ask = none}; + unsubscribed -> + S = Item#roster.subscription, + NS = case S of + both -> to; + from -> none; + _ -> S + end, + % TODO: update presence + Item#roster{subscription = NS, + ask = none} + end, + mnesia:write(NewItem), + {push, NewItem} + end + end, + case mnesia:transaction(F) of + {atomic, ok} -> + ok; + {atomic, {push, Item}} -> + push_item(User, {"", ?MYNAME, ""}, Item), + true; + _ -> + false end. diff --git a/src/sha.erl b/src/sha.erl new file mode 100644 index 000000000..b0339b1aa --- /dev/null +++ b/src/sha.erl @@ -0,0 +1,42 @@ +%%%---------------------------------------------------------------------- +%%% File : sha.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 20 Dec 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(sha). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/0, init/0, sha/1]). + +start() -> + register(sha, spawn(?MODULE, init, [])). + +init() -> + ok = erl_ddll:load_driver(".", sha_erl), + Port = open_port({spawn, sha_erl}, [binary]), + loop(Port). + +loop(Port) -> + receive + {From, {text, Str}} -> + Port ! {self(), {command, Str}}, + SHA = receive + {Port, {data, Bin}} -> + binary_to_term(Bin) + end, + From ! {sha, SHA}, + loop(Port) + end. + +sha(Text) -> + sha ! {self(), {text, Text}}, + receive + {sha, S} -> + S + end. + +