summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sha_erl.c271
-rw-r--r--src/Makefile18
-rw-r--r--src/ejabberd.erl1
-rw-r--r--src/ejabberd_auth.erl26
-rw-r--r--src/ejabberd_c2s.erl49
-rw-r--r--src/ejabberd_router.erl10
-rw-r--r--src/ejabberd_s2s_in.erl63
-rw-r--r--src/ejabberd_sm.erl51
-rw-r--r--src/jlib.erl3
-rw-r--r--src/mod_roster.erl197
-rw-r--r--src/sha.erl42
11 files changed, 631 insertions, 100 deletions
diff --git a/sha_erl.c b/sha_erl.c
new file mode 100644
index 00000000..bce02b76
--- /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 <stdio.h>
+#include <erl_driver.h>
+#include <ei.h>
+
+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 ff57225b..12ff0b40 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 e313cec8..e560d79c 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 4463932c..86b6d3b4 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 60946e4b..ddce27c5 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 b6657eff..e28eeb74 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 ca45cbbc..c032e24f 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 975be95f..ea767a39 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 62910a12..e450b80b 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 197dc29e..6c23a0f1 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
+ {atomic, true} ->
+ true;
+ {atomic, false} ->
+ false;
+ {atomic, {push, Item}} ->
+ push_item(User, {"", ?MYNAME, ""}, Item),
+ true;
+ _ ->
+ 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;
_ ->
- ok
+ false
end.
diff --git a/src/sha.erl b/src/sha.erl
new file mode 100644
index 00000000..b0339b1a
--- /dev/null
+++ b/src/sha.erl
@@ -0,0 +1,42 @@
+%%%----------------------------------------------------------------------
+%%% File : sha.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose :
+%%% Created : 20 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% 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.
+
+