mirror of
https://github.com/processone/ejabberd.git
synced 2024-06-18 22:15:20 +02:00
* ejabberd 1.1.2 final
SVN Revision: 656
This commit is contained in:
commit
e1eef9f84b
|
@ -4058,9 +4058,6 @@ Binary Installer
|
|||
- New binary installer for Windows including all requirements.
|
||||
- Improved installers for Linux and MacOSX (PowerPC)
|
||||
|
||||
|
||||
- Anonymous login bugfixes.
|
||||
|
||||
XMPP Compliancy
|
||||
|
||||
- Some protocol compliance fix have been added, after the Portland XMPP
|
||||
|
@ -4076,6 +4073,7 @@ Miscelanous
|
|||
|
||||
Bugfixes
|
||||
|
||||
- Anonymous login bugfixes.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
|
|
@ -63,9 +63,6 @@ Binary Installer
|
|||
- New binary installer for Windows including all requirements.
|
||||
- Improved installers for Linux and MacOSX (PowerPC)
|
||||
|
||||
|
||||
- Anonymous login bugfixes.
|
||||
|
||||
XMPP Compliancy
|
||||
|
||||
- Some protocol compliance fix have been added, after the Portland XMPP
|
||||
|
@ -81,6 +78,7 @@ Miscelanous
|
|||
|
||||
Bugfixes
|
||||
|
||||
- Anonymous login bugfixes.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
|
|
@ -1,343 +0,0 @@
|
|||
As a special exception, the authors give permission to link this program
|
||||
with the OpenSSL library and distribute the resulting binary.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +0,0 @@
|
|||
Win32 build: Make it possible to compile with +debug_info flag.
|
||||
|
||||
admin interface
|
||||
users management
|
||||
statistics about each user
|
||||
statistics about each connection
|
||||
|
||||
S2S:
|
||||
check "id" attributes in db:verify packets
|
||||
|
||||
make roster set to work in one transaction
|
||||
add traffic shapers to c2s connection before authentification
|
||||
more traffic shapers
|
||||
SNMP
|
|
@ -1,21 +0,0 @@
|
|||
extract_translations - auxiliary tool that extracts lines to be translated
|
||||
from ejabberd source tree.
|
||||
|
||||
Building:
|
||||
erlc extract_translations.erl
|
||||
|
||||
Invoking 1:
|
||||
erl -noinput -s extract_translations -extra dirname message_file
|
||||
|
||||
where dirname is the directory "src" in ejabberd's source tree root,
|
||||
message_file is a file with translated messages (src/msgs/*.msg).
|
||||
|
||||
Result is a list of messages from source files which aren't contained in
|
||||
message file.
|
||||
|
||||
Invoking 2:
|
||||
erl -noinput -s extract_translations -extra -unused dirname message_file
|
||||
|
||||
Result is a list of messages from message file which aren't in source
|
||||
files anymore.
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : extract_translations.erl
|
||||
%%% Author : Sergei Golovan <sgolovan@nes.ru>
|
||||
%%% Purpose : Auxiliary tool for interface/messages translators
|
||||
%%% Created : 23 Apr 2005 by Sergei Golovan <sgolovan@nes.ru>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(extract_translations).
|
||||
-author('sgolovan@nes.ru').
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
|
||||
start() ->
|
||||
ets:new(translations, [named_table, public]),
|
||||
ets:new(files, [named_table, public]),
|
||||
ets:new(vars, [named_table, public]),
|
||||
case init:get_plain_arguments() of
|
||||
["-unused", Dir, File] ->
|
||||
Status = process(Dir, File, unused),
|
||||
halt(Status);
|
||||
[Dir, File] ->
|
||||
Status = process(Dir, File, used),
|
||||
halt(Status);
|
||||
_ ->
|
||||
print_usage(),
|
||||
halt(?STATUS_USAGE)
|
||||
end.
|
||||
|
||||
|
||||
process(Dir, File, Used) ->
|
||||
case load_file(File) of
|
||||
{error, Reason} ->
|
||||
io:format("~s: ~s~n", [File, file:format_error(Reason)]),
|
||||
?STATUS_ERROR;
|
||||
_ ->
|
||||
FileList = find_src_files(Dir),
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_file(Dir, F, Used)
|
||||
end, FileList),
|
||||
case Used of
|
||||
unused ->
|
||||
ets:foldl(fun({Key, _}, _) ->
|
||||
io:format("~p~n", [Key])
|
||||
end, ok, translations);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
?STATUS_SUCCESS
|
||||
end.
|
||||
|
||||
parse_file(Dir, File, Used) ->
|
||||
ets:delete_all_objects(vars),
|
||||
case epp:parse_file(File, [Dir, filename:dirname(File)], []) of
|
||||
{ok, Forms} ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, Forms);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
parse_form(Dir, File, Form, Used) ->
|
||||
case Form of
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_, {string, _, Str}]
|
||||
} ->
|
||||
process_string(Dir, File, Str, Used);
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_, {var, _, Name}]
|
||||
} ->
|
||||
case ets:lookup(vars, Name) of
|
||||
[{_Name, Value}] ->
|
||||
process_string(Dir, File, Value, Used);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
{match,
|
||||
_,
|
||||
{var, _, Name},
|
||||
{string, _, Value}
|
||||
} ->
|
||||
ets:insert(vars, {Name, Value});
|
||||
L when is_list(L) ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, L);
|
||||
T when is_tuple(T) ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, tuple_to_list(T));
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
process_string(_Dir, File, Str, Used) ->
|
||||
case {ets:lookup(translations, Str), Used} of
|
||||
{[{_Key, _Trans}], unused} ->
|
||||
ets:delete(translations, Str);
|
||||
{[{_Key, _Trans}], used} ->
|
||||
ok;
|
||||
{_, used} ->
|
||||
case ets:lookup(files, File) of
|
||||
[{_}] ->
|
||||
ok;
|
||||
_ ->
|
||||
io:format("~n% ~s~n", [File]),
|
||||
ets:insert(files, {File})
|
||||
end,
|
||||
io:format("{~p, \"\"}.~n", [Str]),
|
||||
ets:insert(translations, {Str, ""});
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
load_file(File) ->
|
||||
case file:consult(File) of
|
||||
{ok, Terms} ->
|
||||
lists:foreach(
|
||||
fun({Orig, Trans}) ->
|
||||
case Trans of
|
||||
"" ->
|
||||
ok;
|
||||
_ ->
|
||||
ets:insert(translations, {Orig, Trans})
|
||||
end
|
||||
end, Terms);
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
find_src_files(Dir) ->
|
||||
case file:list_dir(Dir) of
|
||||
{ok, FileList} ->
|
||||
recurse_filelist(
|
||||
lists:map(
|
||||
fun(F) ->
|
||||
filename:join(Dir, F)
|
||||
end, FileList));
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
recurse_filelist(FileList) ->
|
||||
recurse_filelist(FileList, []).
|
||||
|
||||
recurse_filelist([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
|
||||
recurse_filelist([H | T], Acc) ->
|
||||
case file:read_file_info(H) of
|
||||
{ok, #file_info{type = directory}} ->
|
||||
recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc);
|
||||
{ok, #file_info{type = regular}} ->
|
||||
case string:substr(H, string:len(H) - 3) of
|
||||
".erl" ->
|
||||
recurse_filelist(T, [H | Acc]);
|
||||
".hrl" ->
|
||||
recurse_filelist(T, [H | Acc]);
|
||||
_ ->
|
||||
recurse_filelist(T, Acc)
|
||||
end;
|
||||
_ ->
|
||||
recurse_filelist(T, Acc)
|
||||
end.
|
||||
|
||||
|
||||
print_usage() ->
|
||||
io:format(
|
||||
"Usage: extract_translations [-unused] dir file~n"
|
||||
"~n"
|
||||
"Example:~n"
|
||||
" extract_translations . ./msgs/ru.msg~n"
|
||||
).
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Frontend for ejabberd's extract_translations.erl
|
||||
# by Badlop
|
||||
# last updated: 8 December 2005
|
||||
|
||||
while [ "$1" != "" ]
|
||||
do
|
||||
case "$1" in
|
||||
-help)
|
||||
echo "Options:"
|
||||
echo " -lang LANGUAGE"
|
||||
echo " -src FULL_PATH_EJABBERD"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " ./prepare-translation.sh -lang es -src /home/admin/ejabberd"
|
||||
exit 0
|
||||
;;
|
||||
-lang)
|
||||
# This is the language to be extracted
|
||||
LANGU=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-src)
|
||||
# This is the path to the ejabberd source dir
|
||||
EJA_DIR=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "unknown option: '$1 $2'"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Where is Erlang binary
|
||||
ERL=`which erl`
|
||||
|
||||
EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/
|
||||
EXTRACT_ERL=extract_translations.erl
|
||||
EXTRACT_BEAM=extract_translations.beam
|
||||
SRC_DIR=$EJA_DIR/src
|
||||
MSGS_DIR=$SRC_DIR/msgs
|
||||
MSGS_FILE=$LANGU.msg
|
||||
MSGS_FILE2=$LANGU.msg.translate
|
||||
MSGS_PATH=$MSGS_DIR/$MSGS_FILE
|
||||
MSGS_PATH2=$MSGS_DIR/$MSGS_FILE2
|
||||
|
||||
if !([[ -n $EJA_DIR ]])
|
||||
then
|
||||
echo "ejabberd dir does not exist: $EJA_DIR"
|
||||
fi
|
||||
|
||||
if !([[ -x $EXTRACT_BEAM ]])
|
||||
then
|
||||
echo -n "Compiling extract_translations.erl: "
|
||||
sh -c "cd $EXTRACT_DIR; $ERL -compile $EXTRACT_ERL"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -n "Extracting language strings for '$LANGU':"
|
||||
|
||||
echo -n " new..."
|
||||
$ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra $SRC_DIR $MSGS_PATH >$MSGS_PATH.new
|
||||
|
||||
echo -n " old..."
|
||||
$ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra -unused $SRC_DIR $MSGS_PATH >$MSGS_PATH.unused
|
||||
|
||||
cat $MSGS_PATH >$MSGS_PATH2
|
||||
|
||||
echo "" >>$MSGS_PATH2
|
||||
|
||||
cat $MSGS_PATH.new >>$MSGS_PATH2
|
||||
rm $MSGS_PATH.new
|
||||
echo "" >>$MSGS_PATH2
|
||||
|
||||
cat $MSGS_PATH.unused >>$MSGS_PATH2
|
||||
rm $MSGS_PATH.unused
|
||||
|
||||
echo " ok"
|
||||
|
||||
echo ""
|
||||
echo "Process completed."
|
||||
|
||||
echo ""
|
||||
echo " A new file has been created for you, with the current, the new and the deprecated strings:"
|
||||
echo " $MSGS_PATH2"
|
||||
echo ""
|
||||
echo " At the end of that file you will find the strings you must update:"
|
||||
echo " - Untranslated strings are like this: {"March", ""}."
|
||||
echo " To translate the string, add the text inside the commas. Example:"
|
||||
echo " {"March", "Marzo"}."
|
||||
echo " - Old strings that are not used: "Woowoa""
|
||||
echo " Search the entire file for those strings and remove them"
|
||||
echo ""
|
||||
echo " Once you have translated all the strings and removed all the old ones,"
|
||||
echo " rename the file to overwrite the previous one:"
|
||||
echo " $MSGS_PATH"
|
|
@ -1,46 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
all: release pdf html
|
||||
|
||||
release:
|
||||
@echo "Notes for the releaser:"
|
||||
@echo "* Do not forget to add a link to the release notes in guide.tex"
|
||||
@echo "* Do not forget to change the version number in version.tex"
|
||||
@echo "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
|
||||
@read -p "press any key to continue"
|
||||
|
||||
html: guide.html dev.html features.html
|
||||
|
||||
pdf: guide.pdf features.pdf
|
||||
|
||||
clean:
|
||||
rm -f *.aux
|
||||
rm -f *.haux
|
||||
rm -f *.html
|
||||
rm -f *.htoc
|
||||
rm -f *.idx
|
||||
rm -f *.ilg
|
||||
rm -f *.ind
|
||||
rm -f *.log
|
||||
rm -f *.out
|
||||
rm -f *.pdf
|
||||
rm -f *.toc
|
||||
|
||||
guide.html: guide.tex
|
||||
hevea -fix -noiso -pedantic guide.tex
|
||||
|
||||
dev.html: dev.tex
|
||||
hevea -fix -noiso -pedantic dev.tex
|
||||
|
||||
features.html: features.tex
|
||||
hevea -fix -noiso -pedantic features.tex
|
||||
|
||||
guide.pdf: guide.tex
|
||||
pdflatex guide.tex
|
||||
pdflatex guide.tex
|
||||
pdflatex guide.tex
|
||||
makeindex guide.idx
|
||||
pdflatex guide.tex
|
||||
|
||||
features.pdf: features.tex
|
||||
pdflatex features.tex
|
|
@ -1,449 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
|
||||
<HEAD>
|
||||
|
||||
<TITLE>Ejabberd 1.1.2 Developers Guide</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<META name="GENERATOR" content="hevea 1.08">
|
||||
<STYLE type="text/css">
|
||||
.toc{list-style:none;}
|
||||
.title{margin:auto;text-align:center}
|
||||
.center{text-align:center;margin-left:auto;margin-right:auto;}
|
||||
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
|
||||
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
|
||||
DIV TABLE{margin-left:inherit;margin-right:inherit;}
|
||||
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
|
||||
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
|
||||
.part{margin:auto;text-align:center}
|
||||
</STYLE>
|
||||
</HEAD>
|
||||
|
||||
<BODY >
|
||||
<!--HEVEA command line is: hevea -fix -noiso -pedantic dev.tex -->
|
||||
<!--HTMLHEAD-->
|
||||
<!--ENDHTML-->
|
||||
<!--PREFIX <ARG ></ARG>-->
|
||||
<!--CUT DEF section 1 -->
|
||||
|
||||
<BR>
|
||||
<BR>
|
||||
<A NAME="sec:titlepage"></A>
|
||||
|
||||
<TABLE CLASS="title">
|
||||
<TR><TD>
|
||||
<H1 CLASS="titlemain">Ejabberd 1.1.2 Developers Guide</H1>
|
||||
<H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
<A HREF="mailto:alexey@sevcom.net"><TT>mailto:alexey@sevcom.net</TT></A><BR>
|
||||
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3></TD>
|
||||
</TR></TABLE><BR>
|
||||
<BR>
|
||||
<DIV CLASS="center">
|
||||
|
||||
<IMG SRC="logo.png" ALT="logo.png">
|
||||
|
||||
|
||||
<BR>
|
||||
<BR>
|
||||
|
||||
</DIV>
|
||||
<BLOCKQUOTE CLASS="quotation"><I>I can thoroughly recommend ejabberd for ease of setup –
|
||||
Kevin Smith, Current maintainer of the Psi project</I></BLOCKQUOTE>
|
||||
<!--TOC section Contents-->
|
||||
|
||||
<H2 CLASS="section">Contents</H2><!--SEC END -->
|
||||
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc1">1 Introduction</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc2">1.1 Key Features</A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc3">1.2 Additional Features</A>
|
||||
</UL>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc4">2 How it Works</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc5">2.1 Router</A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc6">2.2 Local Router</A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc7">2.3 Session Manager</A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc8">2.4 S2S Manager</A>
|
||||
</UL>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc9">3 XML Representation</A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc10">4 Module <TT>xml</TT></A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc11">5 Module <TT>xml_stream</TT></A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc12">6 Modules</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc13">6.1 Module gen_iq_handler</A>
|
||||
<LI CLASS="li-toc"><A HREF="#htoc14">6.2 Services</A>
|
||||
</UL>
|
||||
</UL>
|
||||
|
||||
<!--TOC section Introduction-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="htoc1">1</A> <A NAME="intro">Introduction</A></H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:intro"></A>
|
||||
|
||||
<TT>ejabberd</TT> is a free and open source instant messaging server written in <A HREF="http://www.erlang.org/">Erlang</A>.<BR>
|
||||
<BR>
|
||||
<TT>ejabberd</TT> is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication.<BR>
|
||||
<BR>
|
||||
<TT>ejabberd</TT> is designed to be a rock-solid and feature rich XMPP server.<BR>
|
||||
<BR>
|
||||
<TT>ejabberd</TT> is suitable for small deployments, whether they need to be scalable or not, as well as extremely big deployments.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection Key Features-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc2">1.1</A> <A NAME="keyfeatures">Key Features</A></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:keyfeatures"></A>
|
||||
|
||||
<TT>ejabberd</TT> is:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Cross-platform: <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.<BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize">Distributed: You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.<BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize">Fault-tolerant: You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.<BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize">Administrator Friendly: <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Comprehensive documentation.
|
||||
<LI CLASS="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows.
|
||||
<LI CLASS="li-itemize">Web interface for administration tasks.
|
||||
<LI CLASS="li-itemize">Shared Roster Groups.
|
||||
<LI CLASS="li-itemize">Command line administration tool.
|
||||
<LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
|
||||
<LI CLASS="li-itemize">Capability to send announce messages.
|
||||
</UL><BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize">Internationalized: <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Translated in 12 languages.
|
||||
<LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
|
||||
</UL><BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize">Open Standards: <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Fully XMPP compliant.
|
||||
<LI CLASS="li-itemize">XML-based protocol.
|
||||
<LI CLASS="li-itemize"><A HREF="http://ejabberd.jabber.ru/protocols">Many JEPs supported</A>.
|
||||
</UL></UL>
|
||||
<!--TOC subsection Additional Features-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc3">1.2</A> <A NAME="addfeatures">Additional Features</A></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:addfeatures"></A>
|
||||
|
||||
Moreover, <TT>ejabberd</TT> comes with a wide range of other state-of-the-art features:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Modular
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Load only the modules you want.
|
||||
<LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Security
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
SASL and STARTTLS for c2s and s2s connections.
|
||||
<LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
|
||||
<LI CLASS="li-itemize">Web interface accessible via HTTPS secure access.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Databases
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Native MySQL support.
|
||||
<LI CLASS="li-itemize">Native PostgreSQL support.
|
||||
<LI CLASS="li-itemize">Mnesia.
|
||||
<LI CLASS="li-itemize">ODBC data storage support.
|
||||
<LI CLASS="li-itemize">Microsoft SQL Server support.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Authentication
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
LDAP and ODBC.
|
||||
<LI CLASS="li-itemize">External Authentication script.
|
||||
<LI CLASS="li-itemize">Internal Authentication.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Others
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Compressing XML streams with Stream Compression (<A HREF="http://www.jabber.org/jeps/jep-0138.html">JEP-0138</A>).
|
||||
<LI CLASS="li-itemize">Interface with networks such as AIM, ICQ and MSN.
|
||||
<LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>).
|
||||
<LI CLASS="li-itemize">IPv6 support both for c2s and s2s connections.
|
||||
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> module with logging.
|
||||
<LI CLASS="li-itemize">Users Directory based on users vCards.
|
||||
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> component.
|
||||
<LI CLASS="li-itemize">Support for virtual hosting.
|
||||
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service.
|
||||
<LI CLASS="li-itemize">IRC transport.
|
||||
</UL>
|
||||
</UL>
|
||||
<!--TOC section How it Works-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="htoc4">2</A> How it Works</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:howitworks"></A>
|
||||
A Jabber domain is served by one or more <TT>ejabberd</TT> nodes. These nodes can
|
||||
be run on different machines that are connected via a network. They all must
|
||||
have the ability to connect to port 4369 of all another nodes, and must have
|
||||
the same magic cookie (see Erlang/OTP documentation, in other words the file
|
||||
<TT>~ejabberd/.erlang.cookie</TT> must be the same on all nodes). This is
|
||||
needed because all nodes exchange information about connected users, S2S
|
||||
connections, registered services, etc...<BR>
|
||||
<BR>
|
||||
Each <TT>ejabberd</TT> node have following modules:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
router;
|
||||
<LI CLASS="li-itemize">local router.
|
||||
<LI CLASS="li-itemize">session manager;
|
||||
<LI CLASS="li-itemize">S2S manager;
|
||||
</UL>
|
||||
<!--TOC subsection Router-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc5">2.1</A> Router</H3><!--SEC END -->
|
||||
|
||||
This module is the main router of Jabber packets on each node. It routes
|
||||
them based on their destinations domains. It has two tables: local and global
|
||||
routes. First, domain of packet destination searched in local table, and if it
|
||||
found, then the packet is routed to appropriate process. If no, then it
|
||||
searches in global table, and is routed to the appropriate <TT>ejabberd</TT> node or
|
||||
process. If it does not exists in either tables, then it sent to the S2S
|
||||
manager.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection Local Router-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc6">2.2</A> Local Router</H3><!--SEC END -->
|
||||
|
||||
This module routes packets which have a destination domain equal to this server
|
||||
name. If destination JID has a non-empty user part, then it routed to the
|
||||
session manager, else it is processed depending on it's content.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection Session Manager-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc7">2.3</A> Session Manager</H3><!--SEC END -->
|
||||
|
||||
This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection S2S Manager-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc8">2.4</A> S2S Manager</H3><!--SEC END -->
|
||||
|
||||
This module routes packets to other Jabber servers. First, it checks if an
|
||||
open S2S connection from the domain of the packet source to the domain of
|
||||
packet destination already exists. If it is open on another node, then it
|
||||
routes the packet to S2S manager on that node, if it is open on this node, then
|
||||
it is routed to the process that serves this connection, and if a connection
|
||||
does not exist, then it is opened and registered.<BR>
|
||||
<BR>
|
||||
<!--TOC section XML Representation-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="htoc9">3</A> XML Representation</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:xmlrepr"></A>
|
||||
Each XML stanza is represented as the following tuple:
|
||||
<PRE CLASS="verbatim">
|
||||
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
Name = string()
|
||||
Attrs = [Attr]
|
||||
Attr = {Key, Val}
|
||||
Key = string()
|
||||
Val = string()
|
||||
ElementOrCDATA = XMLElement | CDATA
|
||||
CDATA = {xmlcdata, string()}
|
||||
</PRE>E. g. this stanza:
|
||||
<PRE CLASS="verbatim">
|
||||
<message to='test@conference.example.org' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
</PRE>is represented as the following structure:
|
||||
<PRE CLASS="verbatim">
|
||||
{xmlelement, "message",
|
||||
[{"to", "test@conference.example.org"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
[{xmlcdata, "test"}]}]}}
|
||||
</PRE>
|
||||
<!--TOC section Module <TT>xml</TT>-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="htoc10">4</A> Module <TT>xml</TT></H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:xmlmod"></A>
|
||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>element_to_string(El) -> string()</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
El = XMLElement
|
||||
</PRE>Returns string representation of XML stanza <TT>El</TT>.<BR>
|
||||
<BR>
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>crypt(S) -> string()</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
S = string()
|
||||
</PRE>Returns string which correspond to <TT>S</TT> with encoded XML special
|
||||
characters.<BR>
|
||||
<BR>
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>remove_cdata(ECList) -> EList</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
ECList = [ElementOrCDATA]
|
||||
EList = [XMLElement]
|
||||
</PRE><TT>EList</TT> is a list of all non-CDATA elements of ECList.<BR>
|
||||
<BR>
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>get_path_s(El, Path) -> Res</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
El = XMLElement
|
||||
Path = [PathItem]
|
||||
PathItem = PathElem | PathAttr | PathCDATA
|
||||
PathElem = {elem, Name}
|
||||
PathAttr = {attr, Name}
|
||||
PathCDATA = cdata
|
||||
Name = string()
|
||||
Res = string() | XMLElement
|
||||
</PRE>If <TT>Path</TT> is empty, then returns <TT>El</TT>. Else sequentially
|
||||
consider elements of <TT>Path</TT>. Each element is one of:
|
||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>{elem, Name}</CODE> <TT>Name</TT> is name of subelement of
|
||||
<TT>El</TT>, if such element exists, then this element considered in
|
||||
following steps, else returns empty string.
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>{attr, Name}</CODE> If <TT>El</TT> have attribute <TT>Name</TT>, then
|
||||
returns value of this attribute, else returns empty string.
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>cdata</CODE> Returns CDATA of <TT>El</TT>.
|
||||
</DL><BR>
|
||||
<BR>
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description">TODO:
|
||||
<PRE CLASS="verbatim">
|
||||
get_cdata/1, get_tag_cdata/1
|
||||
get_attr/2, get_attr_s/2
|
||||
get_tag_attr/2, get_tag_attr_s/2
|
||||
get_subtag/2
|
||||
</PRE></DL>
|
||||
<!--TOC section Module <TT>xml_stream</TT>-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="htoc11">5</A> Module <TT>xml_stream</TT></H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:xmlstreammod"></A>
|
||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>parse_element(Str) -> XMLElement | {error, Err}</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
Str = string()
|
||||
Err = term()
|
||||
</PRE>Parses <TT>Str</TT> using XML parser, returns either parsed element or error
|
||||
tuple.
|
||||
</DL>
|
||||
<!--TOC section Modules-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="htoc12">6</A> Modules</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:emods"></A>
|
||||
<!--TOC subsection Module gen_iq_handler-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc13">6.1</A> Module gen_iq_handler</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:geniqhandl"></A>
|
||||
The module <CODE>gen_iq_handler</CODE> allows to easily write handlers for IQ packets
|
||||
of particular XML namespaces that addressed to server or to users bare JIDs.<BR>
|
||||
<BR>
|
||||
In this module the following functions are defined:
|
||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>add_iq_handler(Component, Host, NS, Module, Function, Type)</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
Component = Module = Function = atom()
|
||||
Host = NS = string()
|
||||
Type = no_queue | one_queue | parallel
|
||||
</PRE>Registers function <CODE>Module:Function</CODE> as handler for IQ packets on
|
||||
virtual host <CODE>Host</CODE> that contain child of namespace <CODE>NS</CODE> in
|
||||
<CODE>Component</CODE>. Queueing discipline is <CODE>Type</CODE>. There are at least
|
||||
two components defined:
|
||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>ejabberd_local</CODE> Handles packets that addressed to server JID;
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>ejabberd_sm</CODE> Handles packets that addressed to users bare JIDs.
|
||||
</DL>
|
||||
<DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>remove_iq_handler(Component, Host, NS)</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
Component = atom()
|
||||
Host = NS = string()
|
||||
</PRE>Removes IQ handler on virtual host <CODE>Host</CODE> for namespace <CODE>NS</CODE> from
|
||||
<CODE>Component</CODE>.
|
||||
</DL>
|
||||
Handler function must have the following type:
|
||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description"><DD CLASS="dd-description"><CODE>Module:Function(From, To, IQ)</CODE>
|
||||
<PRE CLASS="verbatim">
|
||||
From = To = jid()
|
||||
</PRE></DL>
|
||||
<PRE CLASS="verbatim">
|
||||
-module(mod_cputime).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
process_local_iq/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(NS_CPUTIME, "ejabberd:cputime").
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_CPUTIME,
|
||||
?MODULE, process_local_iq, IQDisc).
|
||||
|
||||
stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_CPUTIME).
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
CPUTime = element(1, erlang:statistics(runtime))/1000,
|
||||
SCPUTime = lists:flatten(io_lib:format("~.3f", CPUTime)),
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_CPUTIME}],
|
||||
[{xmlelement, "cputime", [], [{xmlcdata, SCPUTime}]}]}]}
|
||||
end.
|
||||
</PRE>
|
||||
<!--TOC subsection Services-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="htoc14">6.2</A> Services</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:services"></A>
|
||||
<PRE CLASS="verbatim">
|
||||
-module(mod_echo).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, init/1, stop/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
|
||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
spawn(?MODULE, init, [MyHost])).
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_local_route(Host),
|
||||
loop(Host).
|
||||
|
||||
loop(Host) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
ejabberd_router:route(To, From, Packet),
|
||||
loop(Host);
|
||||
stop ->
|
||||
ejabberd_router:unregister_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
Proc ! stop,
|
||||
{wait, Proc}.
|
||||
</PRE>
|
||||
<!--HTMLFOOT-->
|
||||
<!--ENDHTML-->
|
||||
<!--FOOTER-->
|
||||
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
|
||||
</EM><A HREF="http://pauillac.inria.fr/~maranget/hevea/index.html"><EM>H<FONT SIZE=2><sup>E</sup></FONT>V<FONT SIZE=2><sup>E</sup></FONT>A</EM></A><EM>.</EM></BLOCKQUOTE></BODY>
|
||||
</HTML>
|
|
@ -1,398 +0,0 @@
|
|||
\documentclass[a4paper,10pt]{article}
|
||||
|
||||
%% Packages
|
||||
\usepackage{graphics}
|
||||
\usepackage{hevea}
|
||||
\usepackage{makeidx}
|
||||
\usepackage{verbatim}
|
||||
|
||||
%% Index
|
||||
\makeindex
|
||||
% Remove the index anchors from the HTML version to save size and bandwith.
|
||||
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
|
||||
|
||||
%% Images
|
||||
\newcommand{\logoscale}{0.7}
|
||||
\newcommand{\imgscale}{0.58}
|
||||
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
|
||||
\newcommand{\insscaleimg}[2]{
|
||||
\imgsrc{#2}{}
|
||||
\begin{latexonly}
|
||||
\scalebox{#1}{\includegraphics{#2}}
|
||||
\end{latexonly}
|
||||
}
|
||||
|
||||
%% Various
|
||||
\newcommand{\ns}[1]{\texttt{#1}}
|
||||
\newcommand{\ejabberd}{\texttt{ejabberd}}
|
||||
\newcommand{\Jabber}{Jabber}
|
||||
|
||||
%% Modules
|
||||
\newcommand{\module}[1]{\texttt{#1}}
|
||||
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
||||
\newcommand{\modannounce}{\module{mod\_announce}}
|
||||
\newcommand{\modconfigure}{\module{mod\_configure}}
|
||||
\newcommand{\moddisco}{\module{mod\_disco}}
|
||||
\newcommand{\modecho}{\module{mod\_echo}}
|
||||
\newcommand{\modirc}{\module{mod\_irc}}
|
||||
\newcommand{\modlast}{\module{mod\_last}}
|
||||
\newcommand{\modlastodbc}{\module{mod\_last\_odbc}}
|
||||
\newcommand{\modmuc}{\module{mod\_muc}}
|
||||
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
|
||||
\newcommand{\modoffline}{\module{mod\_offline}}
|
||||
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
|
||||
\newcommand{\modprivacy}{\module{mod\_privacy}}
|
||||
\newcommand{\modprivate}{\module{mod\_private}}
|
||||
\newcommand{\modpubsub}{\module{mod\_pubsub}}
|
||||
\newcommand{\modregister}{\module{mod\_register}}
|
||||
\newcommand{\modroster}{\module{mod\_roster}}
|
||||
\newcommand{\modrosterodbc}{\module{mod\_roster\_odbc}}
|
||||
\newcommand{\modservicelog}{\module{mod\_service\_log}}
|
||||
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
|
||||
\newcommand{\modstats}{\module{mod\_stats}}
|
||||
\newcommand{\modtime}{\module{mod\_time}}
|
||||
\newcommand{\modvcard}{\module{mod\_vcard}}
|
||||
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
|
||||
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
|
||||
\newcommand{\modversion}{\module{mod\_version}}
|
||||
|
||||
%% Title page
|
||||
\include{version}
|
||||
\title{Ejabberd \version\ Developers Guide}
|
||||
\author{Alexey Shchepin \\
|
||||
\ahrefurl{mailto:alexey@sevcom.net} \\
|
||||
\ahrefurl{xmpp:aleksey@jabber.ru}}
|
||||
|
||||
%% Options
|
||||
\newcommand{\marking}[1]{#1} % Marking disabled
|
||||
\newcommand{\quoting}[2][yozhik]{} % Quotes disabled
|
||||
\newcommand{\new}{\begin{latexonly}\marginpar{\textsc{new}}\end{latexonly}} % Highlight new features
|
||||
\newcommand{\improved}{\begin{latexonly}\marginpar{\textsc{improved}}\end{latexonly}} % Highlight improved features
|
||||
\newcommand{\moreinfo}[1]{} % Hide details
|
||||
|
||||
%% Footnotes
|
||||
\newcommand{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
|
||||
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\label{sec:titlepage}
|
||||
\begin{titlepage}
|
||||
\maketitle{}
|
||||
|
||||
\begin{center}
|
||||
{\insscaleimg{\logoscale}{logo.png}
|
||||
\par
|
||||
}
|
||||
\end{center}
|
||||
|
||||
\begin{quotation}\textit{I can thoroughly recommend ejabberd for ease of setup --
|
||||
Kevin Smith, Current maintainer of the Psi project}\end{quotation}
|
||||
|
||||
\end{titlepage}
|
||||
|
||||
\tableofcontents{}
|
||||
|
||||
% Input introduction.tex
|
||||
\input{introduction}
|
||||
|
||||
\section{How it Works}
|
||||
\label{sec:howitworks}
|
||||
|
||||
|
||||
A \Jabber{} domain is served by one or more \ejabberd{} nodes. These nodes can
|
||||
be run on different machines that are connected via a network. They all must
|
||||
have the ability to connect to port 4369 of all another nodes, and must have
|
||||
the same magic cookie (see Erlang/OTP documentation, in other words the file
|
||||
\texttt{\~{}ejabberd/.erlang.cookie} must be the same on all nodes). This is
|
||||
needed because all nodes exchange information about connected users, S2S
|
||||
connections, registered services, etc\ldots
|
||||
|
||||
|
||||
|
||||
Each \ejabberd{} node have following modules:
|
||||
\begin{itemize}
|
||||
\item router;
|
||||
\item local router.
|
||||
\item session manager;
|
||||
\item S2S manager;
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\subsection{Router}
|
||||
|
||||
This module is the main router of \Jabber{} packets on each node. It routes
|
||||
them based on their destinations domains. It has two tables: local and global
|
||||
routes. First, domain of packet destination searched in local table, and if it
|
||||
found, then the packet is routed to appropriate process. If no, then it
|
||||
searches in global table, and is routed to the appropriate \ejabberd{} node or
|
||||
process. If it does not exists in either tables, then it sent to the S2S
|
||||
manager.
|
||||
|
||||
|
||||
\subsection{Local Router}
|
||||
|
||||
This module routes packets which have a destination domain equal to this server
|
||||
name. If destination JID has a non-empty user part, then it routed to the
|
||||
session manager, else it is processed depending on it's content.
|
||||
|
||||
|
||||
\subsection{Session Manager}
|
||||
|
||||
This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.
|
||||
|
||||
|
||||
\subsection{S2S Manager}
|
||||
|
||||
This module routes packets to other \Jabber{} servers. First, it checks if an
|
||||
open S2S connection from the domain of the packet source to the domain of
|
||||
packet destination already exists. If it is open on another node, then it
|
||||
routes the packet to S2S manager on that node, if it is open on this node, then
|
||||
it is routed to the process that serves this connection, and if a connection
|
||||
does not exist, then it is opened and registered.
|
||||
|
||||
|
||||
|
||||
|
||||
\section{XML Representation}
|
||||
\label{sec:xmlrepr}
|
||||
|
||||
Each XML stanza is represented as the following tuple:
|
||||
\begin{verbatim}
|
||||
XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
Name = string()
|
||||
Attrs = [Attr]
|
||||
Attr = {Key, Val}
|
||||
Key = string()
|
||||
Val = string()
|
||||
ElementOrCDATA = XMLElement | CDATA
|
||||
CDATA = {xmlcdata, string()}
|
||||
\end{verbatim}
|
||||
E.\,g. this stanza:
|
||||
\begin{verbatim}
|
||||
<message to='test@conference.example.org' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
\end{verbatim}
|
||||
is represented as the following structure:
|
||||
\begin{verbatim}
|
||||
{xmlelement, "message",
|
||||
[{"to", "test@conference.example.org"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
[{xmlcdata, "test"}]}]}}
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
|
||||
\section{Module \texttt{xml}}
|
||||
\label{sec:xmlmod}
|
||||
|
||||
\begin{description}
|
||||
\item{\verb|element_to_string(El) -> string()|}
|
||||
\begin{verbatim}
|
||||
El = XMLElement
|
||||
\end{verbatim}
|
||||
Returns string representation of XML stanza \texttt{El}.
|
||||
|
||||
\item{\verb|crypt(S) -> string()|}
|
||||
\begin{verbatim}
|
||||
S = string()
|
||||
\end{verbatim}
|
||||
Returns string which correspond to \texttt{S} with encoded XML special
|
||||
characters.
|
||||
|
||||
\item{\verb|remove_cdata(ECList) -> EList|}
|
||||
\begin{verbatim}
|
||||
ECList = [ElementOrCDATA]
|
||||
EList = [XMLElement]
|
||||
\end{verbatim}
|
||||
\texttt{EList} is a list of all non-CDATA elements of ECList.
|
||||
|
||||
|
||||
|
||||
\item{\verb|get_path_s(El, Path) -> Res|}
|
||||
\begin{verbatim}
|
||||
El = XMLElement
|
||||
Path = [PathItem]
|
||||
PathItem = PathElem | PathAttr | PathCDATA
|
||||
PathElem = {elem, Name}
|
||||
PathAttr = {attr, Name}
|
||||
PathCDATA = cdata
|
||||
Name = string()
|
||||
Res = string() | XMLElement
|
||||
\end{verbatim}
|
||||
If \texttt{Path} is empty, then returns \texttt{El}. Else sequentially
|
||||
consider elements of \texttt{Path}. Each element is one of:
|
||||
\begin{description}
|
||||
\item{\verb|{elem, Name}|} \texttt{Name} is name of subelement of
|
||||
\texttt{El}, if such element exists, then this element considered in
|
||||
following steps, else returns empty string.
|
||||
\item{\verb|{attr, Name}|} If \texttt{El} have attribute \texttt{Name}, then
|
||||
returns value of this attribute, else returns empty string.
|
||||
\item{\verb|cdata|} Returns CDATA of \texttt{El}.
|
||||
\end{description}
|
||||
|
||||
\item{TODO:}
|
||||
\begin{verbatim}
|
||||
get_cdata/1, get_tag_cdata/1
|
||||
get_attr/2, get_attr_s/2
|
||||
get_tag_attr/2, get_tag_attr_s/2
|
||||
get_subtag/2
|
||||
\end{verbatim}
|
||||
\end{description}
|
||||
|
||||
|
||||
\section{Module \texttt{xml\_stream}}
|
||||
\label{sec:xmlstreammod}
|
||||
|
||||
\begin{description}
|
||||
\item{\verb!parse_element(Str) -> XMLElement | {error, Err}!}
|
||||
\begin{verbatim}
|
||||
Str = string()
|
||||
Err = term()
|
||||
\end{verbatim}
|
||||
Parses \texttt{Str} using XML parser, returns either parsed element or error
|
||||
tuple.
|
||||
\end{description}
|
||||
|
||||
|
||||
\section{Modules}
|
||||
\label{sec:emods}
|
||||
|
||||
|
||||
%\subsection{gen\_mod behaviour}
|
||||
%\label{sec:genmod}
|
||||
|
||||
%TBD
|
||||
|
||||
\subsection{Module gen\_iq\_handler}
|
||||
\label{sec:geniqhandl}
|
||||
|
||||
The module \verb|gen_iq_handler| allows to easily write handlers for IQ packets
|
||||
of particular XML namespaces that addressed to server or to users bare JIDs.
|
||||
|
||||
In this module the following functions are defined:
|
||||
\begin{description}
|
||||
\item{\verb|add_iq_handler(Component, Host, NS, Module, Function, Type)|}
|
||||
\begin{verbatim}
|
||||
Component = Module = Function = atom()
|
||||
Host = NS = string()
|
||||
Type = no_queue | one_queue | parallel
|
||||
\end{verbatim}
|
||||
Registers function \verb|Module:Function| as handler for IQ packets on
|
||||
virtual host \verb|Host| that contain child of namespace \verb|NS| in
|
||||
\verb|Component|. Queueing discipline is \verb|Type|. There are at least
|
||||
two components defined:
|
||||
\begin{description}
|
||||
\item{\verb|ejabberd_local|} Handles packets that addressed to server JID;
|
||||
\item{\verb|ejabberd_sm|} Handles packets that addressed to users bare JIDs.
|
||||
\end{description}
|
||||
\item{\verb|remove_iq_handler(Component, Host, NS)|}
|
||||
\begin{verbatim}
|
||||
Component = atom()
|
||||
Host = NS = string()
|
||||
\end{verbatim}
|
||||
Removes IQ handler on virtual host \verb|Host| for namespace \verb|NS| from
|
||||
\verb|Component|.
|
||||
\end{description}
|
||||
|
||||
Handler function must have the following type:
|
||||
\begin{description}
|
||||
\item{\verb|Module:Function(From, To, IQ)|}
|
||||
\begin{verbatim}
|
||||
From = To = jid()
|
||||
\end{verbatim}
|
||||
\end{description}
|
||||
|
||||
|
||||
|
||||
\begin{verbatim}
|
||||
-module(mod_cputime).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
process_local_iq/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(NS_CPUTIME, "ejabberd:cputime").
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_CPUTIME,
|
||||
?MODULE, process_local_iq, IQDisc).
|
||||
|
||||
stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_CPUTIME).
|
||||
|
||||
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
|
||||
case Type of
|
||||
set ->
|
||||
{iq, ID, error, XMLNS,
|
||||
[SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
CPUTime = element(1, erlang:statistics(runtime))/1000,
|
||||
SCPUTime = lists:flatten(io_lib:format("~.3f", CPUTime)),
|
||||
{iq, ID, result, XMLNS,
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_CPUTIME}],
|
||||
[{xmlelement, "cputime", [], [{xmlcdata, SCPUTime}]}]}]}
|
||||
end.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\subsection{Services}
|
||||
\label{sec:services}
|
||||
|
||||
%TBD
|
||||
|
||||
|
||||
%TODO: use \verb|proc_lib|
|
||||
\begin{verbatim}
|
||||
-module(mod_echo).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, init/1, stop/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
MyHost = gen_mod:get_opt(host, Opts, "echo." ++ Host),
|
||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
spawn(?MODULE, init, [MyHost])).
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_local_route(Host),
|
||||
loop(Host).
|
||||
|
||||
loop(Host) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
ejabberd_router:route(To, From, Packet),
|
||||
loop(Host);
|
||||
stop ->
|
||||
ejabberd_router:unregister_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
Proc ! stop,
|
||||
{wait, Proc}.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
|
||||
\end{document}
|
Binary file not shown.
Before Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.8 KiB |
|
@ -1,163 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
|
||||
<HEAD>
|
||||
|
||||
<TITLE>Ejabberd 1.1.2 Feature Sheet</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<META name="GENERATOR" content="hevea 1.08">
|
||||
<STYLE type="text/css">
|
||||
.toc{list-style:none;}
|
||||
.title{margin:auto;text-align:center}
|
||||
.center{text-align:center;margin-left:auto;margin-right:auto;}
|
||||
.flushleft{text-align:left;margin-left:0ex;margin-right:auto;}
|
||||
.flushright{text-align:right;margin-left:auto;margin-right:0ex;}
|
||||
DIV TABLE{margin-left:inherit;margin-right:inherit;}
|
||||
PRE{text-align:left;margin-left:0ex;margin-right:auto;}
|
||||
BLOCKQUOTE{margin-left:4ex;margin-right:4ex;text-align:left;}
|
||||
.part{margin:auto;text-align:center}
|
||||
SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
|
||||
</STYLE>
|
||||
</HEAD>
|
||||
|
||||
<BODY >
|
||||
<!--HEVEA command line is: hevea -fix -noiso -pedantic features.tex -->
|
||||
<!--HTMLHEAD-->
|
||||
<!--ENDHTML-->
|
||||
<!--PREFIX <ARG ></ARG>-->
|
||||
<!--CUT DEF section 1 -->
|
||||
|
||||
<BR>
|
||||
<BR>
|
||||
<A NAME="titlepage"></A>
|
||||
|
||||
<TABLE CLASS="title">
|
||||
<TR><TD>
|
||||
<H1 CLASS="titlemain">Ejabberd 1.1.2 Feature Sheet</H1>
|
||||
<H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
<A HREF="mailto:s.devrieze@pandora.be"><TT>mailto:s.devrieze@pandora.be</TT></A><BR>
|
||||
<A HREF="xmpp:sander@devrieze.dyndns.org"><TT>xmpp:sander@devrieze.dyndns.org</TT></A></H3></TD>
|
||||
</TR></TABLE><BR>
|
||||
<BR>
|
||||
<DIV CLASS="center">
|
||||
|
||||
<IMG SRC="logo.png" ALT="logo.png">
|
||||
|
||||
|
||||
<BR>
|
||||
<BR>
|
||||
|
||||
</DIV>
|
||||
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>I can thoroughly recommend ejabberd for ease of setup –
|
||||
Kevin Smith, Current maintainer of the Psi project</I></FONT></BLOCKQUOTE>
|
||||
<!--TOC section Introduction-->
|
||||
|
||||
<H2 CLASS="section"><A NAME="intro">Introduction</A></H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:intro"></A>
|
||||
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. —
|
||||
Joeri</I></FONT></BLOCKQUOTE>
|
||||
<TT>ejabberd</TT> is a <B><FONT SIZE=4><FONT COLOR="#001376">free and open source</FONT></FONT></B> instant messaging server written in <A HREF="http://www.erlang.org/">Erlang</A>.<BR>
|
||||
<BR>
|
||||
<TT>ejabberd</TT> is <B><FONT SIZE=4><FONT COLOR="#001376">cross-platform</FONT></FONT></B>, distributed, fault-tolerant, and based on open standards to achieve real-time communication.<BR>
|
||||
<BR>
|
||||
<TT>ejabberd</TT> is designed to be a <B><FONT SIZE=4><FONT COLOR="#001376">rock-solid and feature rich</FONT></FONT></B> XMPP server.<BR>
|
||||
<BR>
|
||||
<TT>ejabberd</TT> is suitable for small deployments, whether they need to be <B><FONT SIZE=4><FONT COLOR="#001376">scalable</FONT></FONT></B> or not, as well as extremely big deployments.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection Key Features-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="keyfeatures">Key Features</A></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:keyfeatures"></A>
|
||||
|
||||
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>Erlang seems to be tailor-made for writing stable, robust servers. —
|
||||
Peter Saint-André, Executive Director of the Jabber Software Foundation</I></FONT></BLOCKQUOTE>
|
||||
<TT>ejabberd</TT> is:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
<B><FONT SIZE=4><FONT COLOR="#001376">Cross-platform:</FONT></FONT></B> <TT>ejabberd</TT> runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.<BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Distributed:</FONT></FONT></B> You can run <TT>ejabberd</TT> on a cluster of machines and all of them will serve the same Jabber domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.<BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Fault-tolerant:</FONT></FONT></B> You can deploy an <TT>ejabberd</TT> cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.<BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Administrator Friendly:</FONT></FONT></B> <TT>ejabberd</TT> is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Comprehensive documentation.
|
||||
<LI CLASS="li-itemize">Straightforward installers for Linux, Mac OS X, and Windows.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
|
||||
<LI CLASS="li-itemize">Web interface for administration tasks.
|
||||
<LI CLASS="li-itemize">Shared Roster Groups.
|
||||
<LI CLASS="li-itemize">Command line administration tool.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
|
||||
<LI CLASS="li-itemize">Can integrate with existing authentication mechanisms.
|
||||
<LI CLASS="li-itemize">Capability to send announce messages.
|
||||
</UL><BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Internationalized:</FONT></FONT></B> <TT>ejabberd</TT> leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Translated in 12 languages.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
|
||||
<LI CLASS="li-itemize">Support for <A HREF="http://www.ietf.org/rfc/rfc3490.txt">IDNA</A>.
|
||||
</UL><BR>
|
||||
<BR>
|
||||
<LI CLASS="li-itemize"><B><FONT SIZE=4><FONT COLOR="#001376">Open Standards:</FONT></FONT></B> <TT>ejabberd</TT> is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Fully XMPP compliant.
|
||||
<LI CLASS="li-itemize">XML-based protocol.
|
||||
<LI CLASS="li-itemize"><A HREF="http://ejabberd.jabber.ru/protocols">Many JEPs supported</A>.
|
||||
</UL></UL>
|
||||
<!--TOC subsection Additional Features-->
|
||||
|
||||
<H3 CLASS="subsection"><A NAME="addfeatures">Additional Features</A></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:addfeatures"></A>
|
||||
|
||||
<BLOCKQUOTE CLASS="quotation"><FONT COLOR="#921700"><I>ejabberd is making inroads to solving the "buggy incomplete server" problem —
|
||||
Justin Karneges, Founder of the Psi and the Delta projects</I></FONT></BLOCKQUOTE>
|
||||
Moreover, <TT>ejabberd</TT> comes with a wide range of other state-of-the-art features:
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Modular
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Load only the modules you want.
|
||||
<LI CLASS="li-itemize">Extend <TT>ejabberd</TT> with your own custom modules.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Security
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
SASL and STARTTLS for c2s and s2s connections.
|
||||
<LI CLASS="li-itemize">STARTTLS and Dialback s2s connections.
|
||||
<LI CLASS="li-itemize">Web interface accessible via HTTPS secure access.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Databases
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Native MySQL support.
|
||||
<LI CLASS="li-itemize">Native PostgreSQL support.
|
||||
<LI CLASS="li-itemize">Mnesia.
|
||||
<LI CLASS="li-itemize">ODBC data storage support.
|
||||
<LI CLASS="li-itemize">Microsoft SQL Server support.<FONT COLOR="red"><SPAN STYLE="font-variant:small-caps">new</SPAN></FONT>
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Authentication
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
LDAP and ODBC.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
|
||||
<LI CLASS="li-itemize">External Authentication script.
|
||||
<LI CLASS="li-itemize">Internal Authentication.
|
||||
</UL>
|
||||
<LI CLASS="li-itemize">Others
|
||||
<UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Compressing XML streams with Stream Compression (<A HREF="http://www.jabber.org/jeps/jep-0138.html">JEP-0138</A>).
|
||||
<LI CLASS="li-itemize">Interface with networks such as AIM, ICQ and MSN.
|
||||
<LI CLASS="li-itemize">Statistics via Statistics Gathering (<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>).
|
||||
<LI CLASS="li-itemize">IPv6 support both for c2s and s2s connections.
|
||||
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> module with logging.<FONT COLOR="#FFAA00"><SPAN STYLE="font-variant:small-caps">improved</SPAN></FONT>
|
||||
<LI CLASS="li-itemize">Users Directory based on users vCards.
|
||||
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> component.
|
||||
<LI CLASS="li-itemize">Support for virtual hosting.
|
||||
<LI CLASS="li-itemize"><A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service.
|
||||
<LI CLASS="li-itemize">IRC transport.
|
||||
</UL>
|
||||
</UL>
|
||||
<!--HTMLFOOT-->
|
||||
<!--ENDHTML-->
|
||||
<!--FOOTER-->
|
||||
<HR SIZE=2><BLOCKQUOTE CLASS="quote"><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
|
||||
</EM><A HREF="http://pauillac.inria.fr/~maranget/hevea/index.html"><EM>H<FONT SIZE=2><sup>E</sup></FONT>V<FONT SIZE=2><sup>E</sup></FONT>A</EM></A><EM>.</EM></BLOCKQUOTE></BODY>
|
||||
</HTML>
|
|
@ -1,136 +0,0 @@
|
|||
\documentclass[a4paper,10pt]{article}
|
||||
|
||||
%% Packages
|
||||
\usepackage{epsfig}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphics}
|
||||
\usepackage{hevea}
|
||||
\usepackage[pdftex,colorlinks,unicode,urlcolor=blue,linkcolor=blue,pdftitle=Ejabberd\
|
||||
Feature\ Sheet,pdfauthor=Sander\
|
||||
Devrieze,pdfsubject=ejabberd,pdfkeywords=ejabberd]{hyperref}
|
||||
\usepackage{verbatim}
|
||||
\usepackage{color}
|
||||
|
||||
%% Index
|
||||
% Remove the index anchors from the HTML version to save size and bandwith.
|
||||
\newcommand{\ind}[1]{\begin{latexonly}\index{#1}\end{latexonly}}
|
||||
|
||||
%% Images
|
||||
\newcommand{\logoscale}{0.7}
|
||||
\newcommand{\imgscale}{0.58}
|
||||
\newcommand{\insimg}[1]{\insscaleimg{\imgscale}{#1}}
|
||||
\newcommand{\insscaleimg}[2]{
|
||||
\imgsrc{#2}{}
|
||||
\begin{latexonly}
|
||||
\scalebox{#1}{\includegraphics{#2}}
|
||||
\end{latexonly}
|
||||
}
|
||||
|
||||
%% Various
|
||||
\newcommand{\bracehack}{\def\{{\char"7B}\def\}{\char"7D}}
|
||||
\newcommand{\titem}[1]{\item[\bracehack\texttt{#1}]}
|
||||
\newcommand{\ns}[1]{\texttt{#1}}
|
||||
\newcommand{\jid}[1]{\texttt{#1}}
|
||||
\newcommand{\option}[1]{\texttt{#1}}
|
||||
\newcommand{\poption}[1]{{\bracehack\texttt{#1}}}
|
||||
\newcommand{\node}[1]{\texttt{#1}}
|
||||
\newcommand{\term}[1]{\texttt{#1}}
|
||||
\newcommand{\shell}[1]{\texttt{#1}}
|
||||
\newcommand{\ejabberd}{\texttt{ejabberd}}
|
||||
\newcommand{\Jabber}{Jabber}
|
||||
|
||||
%% Title page
|
||||
\include{version}
|
||||
\title{Ejabberd \version\ Feature Sheet}
|
||||
\author{Sander Devrieze \\
|
||||
\ahrefurl{mailto:s.devrieze@pandora.be} \\
|
||||
\ahrefurl{xmpp:sander@devrieze.dyndns.org}}
|
||||
|
||||
% Options
|
||||
\newcommand{\marking}[1]{\textbf{\begin{large}\textcolor{ejblue}{#1}\end{large}}} % Marking enabled
|
||||
\newcommand{\quoting}[2][yozhik]{\begin{quotation}\textcolor{#1}{\textit{#2}}\end{quotation}} % Quotes enabled
|
||||
\newcommand{\new}{\marginpar{\textcolor{red}{\textsc{new}}}} % Highlight new features
|
||||
\newcommand{\improved}{\marginpar{\textcolor{orange}{\textsc{improved}}}} % Highlight improved features
|
||||
\setcounter{secnumdepth}{-1} % Disable section numbering
|
||||
|
||||
%% To by-pass errors in the HTML version.
|
||||
\newstyle{SPAN}{width:20\%; float:right; text-align:left; margin-left:auto;}
|
||||
\definecolor{orange} {cmyk}{0.000,0.333,1.000,0.000}
|
||||
|
||||
%% Footnotes
|
||||
\begin{latexonly}
|
||||
\global\parskip=9pt plus 3pt minus 1pt
|
||||
\global\parindent=0pt
|
||||
\gdef\ahrefurl#1{\href{#1}{\texttt{#1}}}
|
||||
\gdef\footahref#1#2{#2\footnote{\href{#1}{\texttt{#1}}}}
|
||||
\end{latexonly}
|
||||
\newcommand{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
|
||||
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
|
||||
|
||||
%% Fancy header
|
||||
\fancyhf{}
|
||||
\pagestyle{fancy}
|
||||
\rhead{\textcolor{ejblue}{The Expandable Jabber Daemon.}}
|
||||
\renewcommand{\headrule}{{\color{ejblue}%
|
||||
\hrule width\headwidth height\headrulewidth \vskip-\headrulewidth}}
|
||||
\lhead{\setlength{\unitlength}{-6mm}
|
||||
\begin{picture}(0,0)
|
||||
\put(5.8,3.25){\includegraphics[width=1.3\textwidth]{yozhikheader.png}}
|
||||
\end{picture}}
|
||||
|
||||
% Official ejabberd colours
|
||||
\definecolor{ejblue} {cmyk}{1.000,0.831,0.000,0.537} %RGB: 0,0,118 HTML: 000076
|
||||
\definecolor{ejgreenyellow}{cmyk}{0.079,0.000,0.275,0.102} %RGB: 209,229,159 HTML: d1e59f
|
||||
\definecolor{ejgreendark} {cmyk}{0.131,0.000,0.146,0.220} %RGB: 166,199,162 HTML: a6c7a2
|
||||
\definecolor{ejgreen} {cmyk}{0.077,0.000,0.081,0.078} %RGB: 216,236,215 HTML: d8ecd7
|
||||
\definecolor{ejgreenwhite} {cmyk}{0.044,0.000,0.048,0.020} %RGB: 239,250,238 HTML: effaee
|
||||
\definecolor{yozhik} {cmyk}{0.000,0.837,1.000,0.424} %RGB: 147,0,0 HTML: 930000
|
||||
|
||||
\begin{document}
|
||||
|
||||
\label{titlepage}
|
||||
\begin{titlepage}
|
||||
\maketitle{}
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\begin{center}
|
||||
{\insscaleimg{\logoscale}{logo.png}
|
||||
\par
|
||||
}
|
||||
\end{center}
|
||||
\quoting{I can thoroughly recommend ejabberd for ease of setup --
|
||||
Kevin Smith, Current maintainer of the Psi project}
|
||||
|
||||
\end{titlepage}
|
||||
|
||||
\newpage
|
||||
% Set the page counter to 2 so that the titlepage and the second page do not
|
||||
% have the same page number. This fixes the PDFLaTeX warning "destination with
|
||||
% the same identifier".
|
||||
\begin{latexonly}
|
||||
\setcounter{page}{2}
|
||||
\pagecolor{ejgreenwhite}
|
||||
\end{latexonly}
|
||||
|
||||
% Input introduction.tex
|
||||
\input{introduction}
|
||||
|
||||
\end{document}
|
||||
|
||||
%% TODO
|
||||
% * illustrations (e.g. screenshot from web interface)
|
||||
% * commented parts
|
||||
% * slides, guide and html version
|
||||
% * cleaning and improving LaTeX code
|
||||
% * key features: something like this (shorter)? (more focussed on Erlang now): "To reach the goal of high
|
||||
% availability, performance and clustering, ejabberd is written in Erlang, a programming language perfectly
|
||||
% suited for this. Besides that, some parts are written in C to also incude the advantages of this language. In
|
||||
% short, ejabberd is a perfect mix of mainly Erlang code, peppered with some C code to get the final touch!"
|
||||
% <picture of a cocktail>
|
||||
% * key features: saying that ejabberd the only XMPP server is that can do real clustering:
|
||||
% http://www.jivesoftware.org/forums/thread.jspa?threadID=14602
|
||||
% "What I find interesting is that *no* XMPP servers truly provide clustering. This includes all the commercial
|
||||
% servers. The one partial exception appears to be ejabberd, which can cluster certain data such as sessions,
|
||||
% but not all services such as MUC."
|
||||
% * try it today: links to migration tutorials
|
|
@ -1,105 +0,0 @@
|
|||
digraph messages {
|
||||
//concentrate=true;
|
||||
subgraph clusterclients {
|
||||
client1 [shape = box];
|
||||
client2 [shape = box];
|
||||
client3 [shape = box];
|
||||
|
||||
style = dashed;
|
||||
label = "Clients";
|
||||
}
|
||||
|
||||
subgraph clusternode1 {
|
||||
subgraph clusterc2s1 {
|
||||
c2s11;
|
||||
c2s12;
|
||||
style = invis;
|
||||
}
|
||||
subgraph clusterservices1 {
|
||||
service11;
|
||||
service12;
|
||||
service13;
|
||||
style = invis;
|
||||
}
|
||||
//subgraph clusters2s1 {
|
||||
//s2s11;
|
||||
//s2s12;
|
||||
//style = invis;
|
||||
//}
|
||||
c2s11 -> auth1;
|
||||
c2s12 -> auth1;
|
||||
auth1 -> c2s11;
|
||||
auth1 -> c2s12;
|
||||
c2s11 -> sm1;
|
||||
c2s11 -> router1;
|
||||
c2s12 -> sm1;
|
||||
c2s12 -> router1;
|
||||
router1 -> local1;
|
||||
router1 -> service11;
|
||||
router1 -> service12;
|
||||
router1 -> service13;
|
||||
router1 -> s2s11;
|
||||
router1 -> s2s12;
|
||||
service11 -> router1;
|
||||
service12 -> router1;
|
||||
service13 -> router1;
|
||||
s2s11 -> router1;
|
||||
s2s12 -> router1;
|
||||
local1 -> sm1;
|
||||
sm1 -> c2s11;
|
||||
sm1 -> c2s12;
|
||||
|
||||
style = dashed;
|
||||
label = "Node1";
|
||||
}
|
||||
|
||||
subgraph clusternode2 {
|
||||
c2s2 -> auth2;
|
||||
auth2 -> c2s2;
|
||||
c2s2 -> sm2;
|
||||
c2s2 -> router2;
|
||||
router2 -> local2;
|
||||
router2 -> service21;
|
||||
router2 -> s2s21;
|
||||
service21 -> router2;
|
||||
s2s21 -> router2;
|
||||
local2 -> sm2;
|
||||
sm2 -> c2s2;
|
||||
|
||||
style = dashed;
|
||||
label = "Node2";
|
||||
}
|
||||
|
||||
|
||||
|
||||
subgraph clusterservers {
|
||||
server1 [shape = box];
|
||||
server2 [shape = box];
|
||||
server3 [shape = box];
|
||||
|
||||
style = dashed;
|
||||
label = "Servers";
|
||||
}
|
||||
|
||||
|
||||
client1 -> c2s11;
|
||||
client2 -> c2s12;
|
||||
client3 -> c2s2;
|
||||
c2s11 -> client1 [constraint=false];
|
||||
c2s12 -> client2 [constraint=false];
|
||||
c2s2 -> client3 [constraint=false];
|
||||
|
||||
s2s11 -> server1 [minlen = 2];
|
||||
s2s12 -> server2 [minlen = 2];
|
||||
s2s21 -> server3 [minlen = 2];
|
||||
server1 -> s2s11 [constraint=false];
|
||||
server2 -> s2s12 [constraint=false];
|
||||
server3 -> s2s21 [constraint=false];
|
||||
|
||||
router1 -> router2;
|
||||
router2 -> router1;
|
||||
sm1 -> sm2;
|
||||
sm2 -> sm1;
|
||||
|
||||
label = "Data Flows";
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,131 +0,0 @@
|
|||
\section{\aname{intro}{Introduction}}
|
||||
\label{sec:intro}
|
||||
|
||||
\quoting{I just tried out ejabberd and was impressed both by ejabberd itself and the language it is written in, Erlang. ---
|
||||
Joeri}
|
||||
|
||||
%ejabberd is a free and open source instant messaging server written in Erlang. ejabberd is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication (Jabber/XMPP).
|
||||
|
||||
\ejabberd{} is a \marking{free and open source} instant messaging server written in \footahref{http://www.erlang.org/}{Erlang}.
|
||||
|
||||
\ejabberd{} is \marking{cross-platform}, distributed, fault-tolerant, and based on open standards to achieve real-time communication.
|
||||
|
||||
\ejabberd{} is designed to be a \marking{rock-solid and feature rich} XMPP server.
|
||||
|
||||
\ejabberd{} is suitable for small deployments, whether they need to be \marking{scalable} or not, as well as extremely big deployments.
|
||||
|
||||
%\subsection{\aname{layout}{Layout with example deployment (title needs a better name)}}
|
||||
%\label{sec:layout}
|
||||
|
||||
%In this section there will be a graphical overview like these:\\
|
||||
%\verb|http://www.tipic.com/var/timp/timp_dep.gif| \\
|
||||
%\verb|http://www.jabber.com/images/jabber_Com_Platform.jpg| \\
|
||||
%\verb|http://www.antepo.com/files/OPN45systemdatasheet.pdf| \\
|
||||
|
||||
%A page full with names of Jabber client that are known to work with ejabberd. \begin{tiny}tiny font\end{tiny}
|
||||
|
||||
%\subsection{\aname{trytoday}{Try It Today}}
|
||||
%\label{sec:trytoday}
|
||||
|
||||
%(Not sure if I will include/finish this section for the next version.)
|
||||
|
||||
%\begin{itemize}
|
||||
%\item Erlang REPOS
|
||||
%\item Packages in distributions
|
||||
%\item Windows binary
|
||||
%\item source tar.gz
|
||||
%\item Migration from Jabberd14 (and so also Jabberd2 because you can migrate from version 2 back to 14) and Jabber Inc. XCP possible.
|
||||
%\end{itemize}
|
||||
|
||||
\newpage
|
||||
\subsection{\aname{keyfeatures}{Key Features}}
|
||||
\label{sec:keyfeatures}
|
||||
\ind{features!key features}
|
||||
|
||||
\quoting{Erlang seems to be tailor-made for writing stable, robust servers. ---
|
||||
Peter Saint-Andr\'e, Executive Director of the Jabber Software Foundation}
|
||||
|
||||
\ejabberd{} is:
|
||||
\begin{itemize}
|
||||
\item \marking{Cross-platform:} \ejabberd{} runs under Microsoft Windows and Unix derived systems such as Linux, FreeBSD and NetBSD.
|
||||
|
||||
\item \marking{Distributed:} You can run \ejabberd{} on a cluster of machines and all of them will serve the same \Jabber{} domain(s). When you need more capacity you can simply add a new cheap node to your cluster. Accordingly, you do not need to buy an expensive high-end machine to support tens of thousands concurrent users.
|
||||
|
||||
\item \marking{Fault-tolerant:} You can deploy an \ejabberd{} cluster so that all the information required for a properly working service will be replicated permanently on all nodes. This means that if one of the nodes crashes, the others will continue working without disruption. In addition, nodes also can be added or replaced `on the fly'.
|
||||
|
||||
\item \marking{Administrator Friendly:} \ejabberd{} is built on top of the Open Source Erlang. As a result you do not need to install an external database, an external web server, amongst others because everything is already included, and ready to run out of the box. Other administrator benefits include:
|
||||
\begin{itemize}
|
||||
\item Comprehensive documentation.
|
||||
\item Straightforward installers for Linux, Mac OS X, and Windows.\improved{}
|
||||
\item Web interface for administration tasks.
|
||||
\item Shared Roster Groups.
|
||||
\item Command line administration tool.\improved{}
|
||||
\item Can integrate with existing authentication mechanisms.
|
||||
\item Capability to send announce messages.
|
||||
\end{itemize}
|
||||
|
||||
\item \marking{Internationalized:} \ejabberd{} leads in internationalization. Hence it is very well suited in a globalized world. Related features are:
|
||||
\begin{itemize}
|
||||
\item Translated in 12 languages.\improved{}
|
||||
\item Support for \footahref{http://www.ietf.org/rfc/rfc3490.txt}{IDNA}.
|
||||
\end{itemize}
|
||||
|
||||
\item \marking{Open Standards:} \ejabberd{} is the first Open Source Jabber server claiming to fully comply to the XMPP standard.
|
||||
\begin{itemize}
|
||||
\item Fully XMPP compliant.
|
||||
\item XML-based protocol.
|
||||
\item \footahref{http://ejabberd.jabber.ru/protocols}{Many JEPs supported}.
|
||||
\end{itemize}
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\newpage
|
||||
|
||||
\subsection{\aname{addfeatures}{Additional Features}}
|
||||
\label{sec:addfeatures}
|
||||
\ind{features!additional features}
|
||||
|
||||
\quoting{ejabberd is making inroads to solving the "buggy incomplete server" problem ---
|
||||
Justin Karneges, Founder of the Psi and the Delta projects}
|
||||
|
||||
Moreover, \ejabberd{} comes with a wide range of other state-of-the-art features:
|
||||
\begin{itemize}
|
||||
\item Modular
|
||||
\begin{itemize}
|
||||
\item Load only the modules you want.
|
||||
\item Extend \ejabberd{} with your own custom modules.
|
||||
\end{itemize}
|
||||
\item Security
|
||||
\begin{itemize}
|
||||
\item SASL and STARTTLS for c2s and s2s connections.
|
||||
\item STARTTLS and Dialback s2s connections.
|
||||
\item Web interface accessible via HTTPS secure access.
|
||||
\end{itemize}
|
||||
\item Databases
|
||||
\begin{itemize}
|
||||
\item Native MySQL support.
|
||||
\item Native PostgreSQL support.
|
||||
\item Mnesia.
|
||||
\item ODBC data storage support.
|
||||
\item Microsoft SQL Server support.\new{}
|
||||
\end{itemize}
|
||||
\item Authentication
|
||||
\begin{itemize}
|
||||
\item LDAP and ODBC.\improved{}
|
||||
\item External Authentication script.
|
||||
\item Internal Authentication.
|
||||
\end{itemize}
|
||||
\item Others
|
||||
\begin{itemize}
|
||||
\item Compressing XML streams with Stream Compression (\jepref{0138}).
|
||||
\item Interface with networks such as AIM, ICQ and MSN.
|
||||
\item Statistics via Statistics Gathering (\jepref{0039}).
|
||||
\item IPv6 support both for c2s and s2s connections.
|
||||
\item \tjepref{0045}{Multi-User Chat} module with logging.\improved{}
|
||||
\item Users Directory based on users vCards.
|
||||
\item \tjepref{0060}{Publish-Subscribe} component.
|
||||
\item Support for virtual hosting.
|
||||
\item \tjepref{0025}{HTTP Polling} service.
|
||||
\item IRC transport.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
Binary file not shown.
Before Width: | Height: | Size: 81 KiB |
|
@ -1,62 +0,0 @@
|
|||
Release notes
|
||||
ejabberd 0.9.1
|
||||
|
||||
This document describes the main changes from [25]ejabberd 0.9.
|
||||
|
||||
The code can be downloaded from the [26]download page.
|
||||
|
||||
For more detailed information, please refer to ejabberd [27]User Guide.
|
||||
|
||||
|
||||
Groupchat (Multi-user chat and IRC) improvements
|
||||
|
||||
The multi-user chat code has been improved to comply with the latest version
|
||||
of Jabber Enhancement Proposal 0045.
|
||||
|
||||
The IRC (Internet Relay Chat) features now support WHOIS and USERINFO
|
||||
requests.
|
||||
|
||||
|
||||
Web interface
|
||||
|
||||
ejabberd modules management features have been added to the web interface.
|
||||
They now allow to start or stop extension module without restarting the
|
||||
ejabberd server.
|
||||
|
||||
|
||||
Publish and subscribe
|
||||
|
||||
It is now possible to a subscribe node with a JabberID that includes a
|
||||
resource.
|
||||
|
||||
|
||||
Translations
|
||||
|
||||
A new script has been included to help translate ejabberd into new languages
|
||||
and maintain existing translations.
|
||||
|
||||
As a result, ejabberd is now translating into 10 languages:
|
||||
* Dutch
|
||||
* English
|
||||
* French
|
||||
* German
|
||||
* Polish
|
||||
* Portuguese
|
||||
* Russian
|
||||
* Spanish
|
||||
* Swedish
|
||||
* Ukrainian
|
||||
|
||||
|
||||
Migration
|
||||
|
||||
No changes have been made to the database. No particular conversion steps
|
||||
are needed. However, you should backup your database before upgrading to a
|
||||
new ejabberd version.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
This release contains several bugfixes and architectural changes. Please
|
||||
refer to the Changelog file supplied with this release for details of all
|
||||
improvements in the ejabberd code.
|
|
@ -1,99 +0,0 @@
|
|||
Release notes
|
||||
ejabberd 0.9.8
|
||||
2005-08-01
|
||||
|
||||
This document describes the main changes in ejabberd 0.9.8. This
|
||||
version prepares the way for the release of ejabberd 1.0, which
|
||||
is due later this year.
|
||||
|
||||
The code can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
For more detailed information, please refer to ejabberd User Guide
|
||||
on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
|
||||
Recent changes include....
|
||||
|
||||
|
||||
Enhanced virtual hosting
|
||||
|
||||
Virtual hosting applies to many more setting options and
|
||||
features and is transparent. Virtual hosting accepts different
|
||||
parameters for different virtual hosts regarding the following
|
||||
features: authentication method, access control lists and access
|
||||
rules, users management, statistics, and shared roster. The web
|
||||
interface gives access to each virtual host's parameters.
|
||||
|
||||
|
||||
Enhanced Publish-Subscribe module
|
||||
|
||||
ejabberd's Publish-Subscribe module integrates enhancements
|
||||
coming from J-EAI, an XMPP-based integration server built on
|
||||
ejabberd. ejabberd thus supports Publish-Subscribe node
|
||||
configuration. It is possible to define nodes that should be
|
||||
persistent, and the number of items to persist. Besides that, it
|
||||
is also possible to define various notification parameters, such
|
||||
as the delivery of the payload with the notifications, and the
|
||||
notification of subscribers when some changes occur on items.
|
||||
Other examples are: the maximum size of the items payload, the
|
||||
subscription approvers, the limitation of the notification to
|
||||
online users only, etc.
|
||||
|
||||
|
||||
Code reorganisation and update
|
||||
|
||||
- The mod_register module has been cleaned up.
|
||||
- ODBC support has been updated and several bugs have been fixed.
|
||||
|
||||
|
||||
Development API
|
||||
|
||||
To ease the work of Jabber/XMPP developers, a filter_packet hook
|
||||
has been added. As a result it is possible to develop plugins to
|
||||
filter or modify packets flowing through ejabberd.
|
||||
|
||||
|
||||
Translations
|
||||
|
||||
- Translations have been updated to support the new Publish-Subscribe features.
|
||||
- A new Brazilian Portuguese translation has been contributed.
|
||||
|
||||
|
||||
Web interface
|
||||
|
||||
- The CSS stylesheet from the web interface is W3C compliant.
|
||||
|
||||
|
||||
Installers
|
||||
|
||||
Installers are provided for Microsoft Windows and Linux/x86. The
|
||||
Linux installer includes Erlang ASN.1 modules for LDAP
|
||||
authentication support.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains several bugfixes and architectural
|
||||
changes. Among other bugfixes include improvements in LDAP
|
||||
authentication. Please refer to the ChangeLog file supplied
|
||||
with this release regarding all improvements in ejabberd.
|
||||
|
||||
|
||||
References
|
||||
|
||||
The ejabberd feature sheet helps comparing with other Jabber/XMPP
|
||||
servers:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs/features.pdf
|
||||
|
||||
Contributed tutorials of interest are:
|
||||
- Migration from Jabberd1.4 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd1-to-ejabberd
|
||||
- Migration from Jabberd2 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd2-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
|
||||
END
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
Release notes
|
||||
ejabberd 0.9
|
||||
|
||||
This document describes the major new features of and changes to
|
||||
ejabberd 0.9, compared to latest public release ejabber 0.7.5.
|
||||
|
||||
For more detailed information, please refer to ejabberd User
|
||||
Guide.
|
||||
|
||||
|
||||
Virtual Hosting
|
||||
|
||||
ejabberd now can host several domain on the same instance.
|
||||
This option is enabled by using:
|
||||
|
||||
{hosts, ["erlang-projects.org", "erlang-fr.org"]}.
|
||||
|
||||
instead of the previous host directive.
|
||||
|
||||
Note that you are now using a list of hosts. The main one should
|
||||
be the first listed. See migration section further in this release
|
||||
note for details.
|
||||
|
||||
|
||||
Shared Roster
|
||||
|
||||
Shared roster is a new feature that allow the ejabberd
|
||||
administrator to add jabber user that will be present in the
|
||||
roster of every users on the server.
|
||||
Shared roster are enabled by adding:
|
||||
|
||||
{mod_shared_roster, []}
|
||||
|
||||
at the end of your module list in your ejabberd.cfg file.
|
||||
|
||||
|
||||
PostgreSQL (ODBC) support
|
||||
|
||||
This feature is experimental and not yet properly documented. This
|
||||
feature is released for testing purpose.
|
||||
|
||||
You need to have Erlang/OTP R10 to compile with ODBC on various
|
||||
flavour of *nix. You should use Erlang/OTP R10B-4, as this task
|
||||
has became easier with this release. It comes already build in
|
||||
Erlang/OTP Microsoft Windows binary.
|
||||
|
||||
PostgreSQL support is enabled by using the following module in
|
||||
ejabberd.cfg instead of their standard counterpart:
|
||||
|
||||
mod_last_odbc.erl
|
||||
mod_offline_odbc.erl
|
||||
mod_roster_odbc.erl
|
||||
|
||||
The database schema is located in the src/odbc/pq.sql file.
|
||||
|
||||
Look at the src/ejabberd.cfg.example file for more information on
|
||||
how to configure ejabberd with odbc support. You can get support
|
||||
on how to configure ejabberd with a relational database.
|
||||
|
||||
|
||||
Migration from ejabberd 0.7.5
|
||||
|
||||
Migration is pretty straightforward as Mnesia database schema
|
||||
conversions is handled automatically. Remember however that you
|
||||
must backup your ejabberd database before migration.
|
||||
|
||||
Here are the following steps to proceed:
|
||||
|
||||
1. Stop your instance of ejabberd.
|
||||
|
||||
2. In ejabberd.cfg, define the host lists. Change the host
|
||||
directive to the hosts one:
|
||||
Before:
|
||||
{host, "erlang-projects.org"}.
|
||||
After:
|
||||
{hosts, ["erlang-projects.org", "erlang-fr.org"]}.
|
||||
Note that when you restart the server the existing users will be
|
||||
affected to the first virtual host, so the order is important. You
|
||||
should keep the previous hostname as the first virtual host.
|
||||
|
||||
3. Restart ejabberd.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
This release contains several bugfixes and architectural changes.
|
||||
Please refer to the Changelog file supplied with this release for
|
||||
details of all improvements in the ejabberd code.
|
|
@ -1,120 +0,0 @@
|
|||
Release Notes
|
||||
ejabberd 1.0.0
|
||||
14 December 2005
|
||||
|
||||
This document describes the main changes in ejabberd 1.0.0. Unique in this
|
||||
version is the compliancy with the XMPP (eXtensible Messaging and Presence
|
||||
Protocol) standard. ejabberd is the first Open Source Jabber server claiming
|
||||
to fully comply to the XMPP standard.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the ejabberd Feature Sheet and User
|
||||
Guide which are available on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
Server-to-server Encryption for Enhanced Security
|
||||
|
||||
- Support for STARTTLS and SASL EXTERNAL to secure server-to-server traffic
|
||||
has been added.
|
||||
- Also, STARTTLS and Dialback has been implemented for server-to-server (s2s)
|
||||
connections. Detailed information about these new features can be found on
|
||||
http://ejabberd.jabber.ru/s2s-encryption
|
||||
- commonName and dNSName fields matching were introduced to ease the process
|
||||
of retrieving certificates.
|
||||
- Different certificates can be defined for each virtual host.
|
||||
|
||||
ODBC Support
|
||||
|
||||
- ODBC support has been improved to allow production use of ejabberd with
|
||||
relational databases.
|
||||
- Support for vCard storage in ODBC has been added.
|
||||
- ejd2odbc.erl is a tool to convert an installation from Erlang's database
|
||||
Mnesia to an ODBC compatible relational database.
|
||||
|
||||
Native PostgreSQL Support
|
||||
|
||||
- Native PostgreSQL support gives you a better performance when you use
|
||||
PostgreSQL.
|
||||
|
||||
Shared Roster groups
|
||||
|
||||
- Shared Roster groups support has been enhanced. New is the ability to add
|
||||
all registered users to everyone's roster. Detailed information about this
|
||||
new feature can be found on http://ejabberd.jabber.ru/shared-roster-all
|
||||
|
||||
Web Interface
|
||||
|
||||
- The web interface internal code has been modified for better integration
|
||||
and compliancy with J-EAI, an ejabberd-based Enterprise Application
|
||||
Integration platform.
|
||||
- More XHTML 1.0 Transitional compliancy work was done.
|
||||
|
||||
Transports
|
||||
|
||||
- A transport workaround can be enabled during compilation. To do this, you
|
||||
can pass the "--enable-roster-gateway-workaround" option to the configure
|
||||
script. (./configure --enable-roster-gateway-workaround)
|
||||
This option allows transports to add items with subscription "to" in the
|
||||
roster by sending <presence type='subscribed'/> stanza to user. This option
|
||||
is only needed for JIT ICQ transport.
|
||||
Warning: by enabling this option, ejabberd will not be fully XMPP compliant
|
||||
anymore.
|
||||
|
||||
Documentation and Internationalization
|
||||
|
||||
- Documentation has been extended to cover more topics.
|
||||
- Translations have been updated.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains several bugfixes.
|
||||
- Among other bugfixes include improvements to the client-to-server (c2s)
|
||||
connection management module.
|
||||
- Please refer to the ChangeLog file supplied
|
||||
with this release regarding all improvements in ejabberd.
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C to run ejabberd 1.0.0.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows and Linux/x86.
|
||||
Installers can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- When upgrading an ODBC-based installation, you will need to change the
|
||||
relational database schema. The following SQL commands must be run on the
|
||||
database:
|
||||
CREATE SEQUENCE spool_seq_seq;
|
||||
ALTER TABLE spool ADD COLUMN seq integer;
|
||||
ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
|
||||
UPDATE spool SET seq = DEFAULT;
|
||||
ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials of interest are:
|
||||
- Migration from Jabberd1.4 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd1-to-ejabberd
|
||||
- Migration from Jabberd2 to ejabberd:
|
||||
http://ejabberd.jabber.ru/jabberd2-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
|
||||
END
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
Release Notes
|
||||
ejabberd 1.1.0
|
||||
24 April 2006
|
||||
|
||||
This document describes the main changes in ejabberd 1.1.0. This version
|
||||
introduce new features including support for new Jabber Enhancement
|
||||
Proposals and several performance improvements enabling deployments on an
|
||||
even larger scale than already possible.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the ejabberd Feature Sheet and User
|
||||
Guide which are available on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
A complete list of changes is available from:
|
||||
http://support.process-one.net/secure/ReleaseNote.jspa?projectId=10011&styleName=Html&version=10025
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
New Jabber Enhancement Proposal support:
|
||||
|
||||
- JEP-0050: Ad-Hoc Commands.
|
||||
- JEP-0138: Stream Compression.
|
||||
- JEP-0175: SASL anonymous.
|
||||
|
||||
Anonymous login
|
||||
|
||||
- SASL anonymous.
|
||||
- Anonymous login for clients that do not yet support SASL Anonymous.
|
||||
|
||||
Relational database Support
|
||||
|
||||
- MySQL is now fully supported through ODBC and in native mode.
|
||||
- Various improvements to the native database interfaces.
|
||||
- The migration tool can use relational databases.
|
||||
|
||||
Multi-User Chat improvements
|
||||
|
||||
- Logging of room discussion to text file is now supported.
|
||||
- Better reconfiguration support.
|
||||
- Security oriented fixes.
|
||||
- Several improvements and updates to latest JEP-0045.
|
||||
|
||||
Performance scalability improvements for large clusters
|
||||
|
||||
- Improved session synchronisation management between cluster nodes.
|
||||
- Internal architecture has been reworked to use generalize Erlang/OTP
|
||||
framework usage.
|
||||
- Speed improvement on logger.
|
||||
- TCP/IP packet reception change for better network throttling and
|
||||
regulation.
|
||||
As a result, these improvements will reduce load on large scale deployments.
|
||||
|
||||
XMPP Protocol related improvements
|
||||
|
||||
- XML stanza size can be limited.
|
||||
- Messages are send to all resources with the same highest priority.
|
||||
|
||||
Documentation and Internationalization
|
||||
|
||||
- Documentation has been extended to cover more topics.
|
||||
- Translations have been updated.
|
||||
|
||||
Web interface
|
||||
|
||||
- XHTML 1.0 compliance.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains many bugfixes on various areas such as Publish-Subscribe, build
|
||||
chain, installers, IRC gateway, ejabberdctl, amongst others.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.0.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
|
||||
Installers can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- The database schema has not been changed comparing to version 1.0.0 and
|
||||
consequently it does not require any migration.
|
||||
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials and documents of interest are:
|
||||
- Migration from Jabberd1.4, Jabberd2 and WPJabber to ejabberd:
|
||||
http://ejabberd.jabber.ru/migrate-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
- Using ejabberd with MySQL native driver:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver
|
||||
- Anonymous User Support:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support
|
||||
- Frequently Asked Questions:
|
||||
http://ejabberd.jabber.ru/faq
|
||||
|
||||
END
|
|
@ -1,119 +0,0 @@
|
|||
Release Notes
|
||||
ejabberd 1.1.1
|
||||
28 April 2006
|
||||
|
||||
This document describes the main changes in ejabberd 1.1.x. This version
|
||||
introduce new features including support for new Jabber Enhancement
|
||||
Proposals and several performance improvements enabling deployments on an
|
||||
even larger scale than already possible.
|
||||
|
||||
This release fix a security issue introduced in ejabberd 1.1.0. In SASL
|
||||
mode, anonymous login was enabled as a default. Upgrading ejabberd 1.1.0 to
|
||||
ejabberd 1.1.1 is highly recommended.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the ejabberd Feature Sheet and User
|
||||
Guide which are available on the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
A complete list of changes is available from:
|
||||
http://support.process-one.net/secure/ReleaseNote.jspa?projectId=10011&styleName=Html&version=10025
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
|
||||
New Jabber Enhancement Proposal support:
|
||||
|
||||
- JEP-0050: Ad-Hoc Commands.
|
||||
- JEP-0138: Stream Compression.
|
||||
- JEP-0175: SASL anonymous.
|
||||
|
||||
Anonymous login
|
||||
|
||||
- SASL anonymous.
|
||||
- Anonymous login for clients that do not yet support SASL Anonymous.
|
||||
|
||||
Relational database Support
|
||||
|
||||
- MySQL is now fully supported through ODBC and in native mode.
|
||||
- Various improvements to the native database interfaces.
|
||||
- The migration tool can use relational databases.
|
||||
|
||||
Multi-User Chat improvements
|
||||
|
||||
- Logging of room discussion to text file is now supported.
|
||||
- Better reconfiguration support.
|
||||
- Security oriented fixes.
|
||||
- Several improvements and updates to latest JEP-0045.
|
||||
|
||||
Performance scalability improvements for large clusters
|
||||
|
||||
- Improved session synchronisation management between cluster nodes.
|
||||
- Internal architecture has been reworked to use generalize Erlang/OTP
|
||||
framework usage.
|
||||
- Speed improvement on logger.
|
||||
- TCP/IP packet reception change for better network throttling and
|
||||
regulation.
|
||||
As a result, these improvements will reduce load on large scale deployments.
|
||||
|
||||
XMPP Protocol related improvements
|
||||
|
||||
- XML stanza size can be limited.
|
||||
- Messages are send to all resources with the same highest priority.
|
||||
|
||||
Documentation and Internationalization
|
||||
|
||||
- Documentation has been extended to cover more topics.
|
||||
- Translations have been updated.
|
||||
|
||||
Web interface
|
||||
|
||||
- XHTML 1.0 compliance.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- This release contains many bugfixes on various areas such as Publish-Subscribe, build
|
||||
chain, installers, IRC gateway, ejabberdctl, amongst others.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.0.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
|
||||
Installers can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- The database schema has not been changed comparing to version 1.0.0 and
|
||||
consequently it does not require any migration.
|
||||
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials and documents of interest are:
|
||||
- Migration from Jabberd1.4, Jabberd2 and WPJabber to ejabberd:
|
||||
http://ejabberd.jabber.ru/migrate-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
- Using ejabberd with MySQL native driver:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver
|
||||
- Anonymous User Support:
|
||||
http://support.process-one.net/doc/display/MESSENGER/Anonymous+users+support
|
||||
- Frequently Asked Questions:
|
||||
http://ejabberd.jabber.ru/faq
|
||||
|
||||
END
|
|
@ -1,119 +0,0 @@
|
|||
Release Notes
|
||||
ejabberd 1.1.2
|
||||
27 September 2006
|
||||
|
||||
This document describes the main changes in ejabberd 1.1.2.
|
||||
|
||||
This version is a major improvement over ejabberd 1.1.1, improving the
|
||||
overall behaviour of the server in many areas. Users of ejabberd 1.1.1
|
||||
should upgrade to this new release for improved robustness and compliance.
|
||||
|
||||
ejabberd can be downloaded from the Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/
|
||||
|
||||
Detailed information can be found in the Feature Sheet and in the
|
||||
Installation and Operation Guide which are both available on the
|
||||
Process-one website:
|
||||
http://www.process-one.net/en/projects/ejabberd/docs.html
|
||||
|
||||
ejabberd includes 44 improvements. A complete list of changes can be
|
||||
retrieved from:
|
||||
http://redir.process-one.net/ejabberd-1.1.2
|
||||
|
||||
|
||||
Recent changes include:
|
||||
|
||||
LDAP Improvements
|
||||
|
||||
- Major improvements have been made on the LDAP module. It is now more
|
||||
flexible and more robust.
|
||||
|
||||
HTTP Polling Fixes
|
||||
|
||||
- The HTTP polling modules have been fixed and improved: the connections are
|
||||
closed properly and polled messages cannot be lost anymore.
|
||||
|
||||
Roster Management Improvement
|
||||
|
||||
- Roster management improvements increase reliability, especially in cases
|
||||
where users are on different servers.
|
||||
- Shared rosters are more reliable.
|
||||
|
||||
Improved Robustness
|
||||
|
||||
- It is now possible to limit the number of opened connections for a single
|
||||
user.
|
||||
|
||||
Relational databases
|
||||
|
||||
- Database support: Microsoft SQL Server is now officially supported in ODBC
|
||||
mode.
|
||||
|
||||
Publish-Subscribe Improvement
|
||||
|
||||
- Restricting node creation with a dedicated ACL rule is now possible.
|
||||
|
||||
Localization
|
||||
|
||||
- A Czech translation has been added.
|
||||
- Translations have been updated.
|
||||
|
||||
Binary Installer
|
||||
|
||||
- New binary installer for Windows including all requirements.
|
||||
- Improved installers for Linux and MacOSX (PowerPC)
|
||||
|
||||
XMPP Compliancy
|
||||
|
||||
- Some protocol compliance fix have been added, after the Portland XMPP
|
||||
Interop Meeting in July.
|
||||
|
||||
Miscelanous
|
||||
|
||||
- MUC have been improved (logging rendering).
|
||||
- The command line tool ejabberdctl has been improved.
|
||||
- The build chain has been improved, including MacOSX support.
|
||||
- The documentation has been improved and updated to describe the new
|
||||
features.
|
||||
|
||||
Bugfixes
|
||||
|
||||
- Anonymous login bugfixes.
|
||||
- Please refer to the ChangeLog file supplied with this release regarding
|
||||
all improvements in ejabberd.
|
||||
|
||||
|
||||
Installation Notes
|
||||
|
||||
Supported Erlang Version
|
||||
|
||||
- You need at least Erlang/OTP R9C-2 to run ejabberd 1.1.2.
|
||||
- The recommanded version is Erlang/OTP R10B-10.
|
||||
- Erlang/OTP R11B has not yet been fully certified for ejabberd.
|
||||
|
||||
Installation
|
||||
|
||||
Installers are provided for Microsoft Windows, Linux/x86 and MacOSX/PPC.
|
||||
They can be retrieved from:
|
||||
http://www.process-one.net/en/projects/ejabberd/download.html
|
||||
|
||||
Migration Notes
|
||||
|
||||
- Before any migration, ejabberd system and database must be properly
|
||||
backed up.
|
||||
- The relational database schema has changed between version 1.1.1 and
|
||||
1.1.2. An "askmessage" column needs to be added in the "rosterusers" table
|
||||
to perform the migration.
|
||||
|
||||
|
||||
References
|
||||
|
||||
Contributed tutorials and documents of interest are:
|
||||
- Migration from other XMPP servers to ejabberd:
|
||||
http://ejabberd.jabber.ru/migrate-to-ejabberd
|
||||
- Transport configuration for connecting to other networks:
|
||||
http://ejabberd.jabber.ru/tutorials-transports
|
||||
- Frequently Asked Questions:
|
||||
http://ejabberd.jabber.ru/faq
|
||||
|
||||
END
|
|
@ -1,2 +0,0 @@
|
|||
% Define ejabberd version here.
|
||||
\newcommand{\version}{1.1.2}
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 157 KiB |
|
@ -1,50 +0,0 @@
|
|||
#!/usr/local/bin/perl
|
||||
|
||||
use Unix::Syslog qw(:macros :subs);
|
||||
|
||||
my $domain = $ARGV[0] || "example.com";
|
||||
|
||||
while(1)
|
||||
{
|
||||
# my $rin = '',$rout;
|
||||
# vec($rin,fileno(STDIN),1) = 1;
|
||||
# $ein = $rin;
|
||||
# my $nfound = select($rout=$rin,undef,undef,undef);
|
||||
|
||||
my $buf = "";
|
||||
syslog LOG_INFO,"waiting for packet";
|
||||
my $nread = sysread STDIN,$buf,2;
|
||||
do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2;
|
||||
my $len = unpack "n",$buf;
|
||||
my $nread = sysread STDIN,$buf,$len;
|
||||
|
||||
my ($op,$user,$password) = split /:/,$buf;
|
||||
#$user =~ s/\./\//og;
|
||||
my $jid = "$user\@$domain";
|
||||
my $result;
|
||||
|
||||
syslog(LOG_INFO,"request (%s)", $op);
|
||||
|
||||
SWITCH:
|
||||
{
|
||||
$op eq 'auth' and do
|
||||
{
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'setpass' and do
|
||||
{
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'isuser' and do
|
||||
{
|
||||
# password is null. Return 1 if the user $user\@$domain exitst.
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
};
|
||||
my $out = pack "nn",2,$result ? 1 : 0;
|
||||
syswrite STDOUT,$out;
|
||||
}
|
||||
|
||||
closelog;
|
|
@ -1,75 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# PROVIDE: ejabberd
|
||||
# REQUIRE: DAEMON
|
||||
# KEYWORD: shutdown
|
||||
#
|
||||
|
||||
HOME=/usr/pkg/jabber D=/usr/pkg/jabber/ejabberd export HOME
|
||||
|
||||
name="ejabberd"
|
||||
rcvar=$name
|
||||
|
||||
if [ -r /etc/rc.conf ]
|
||||
then
|
||||
. /etc/rc.conf
|
||||
else
|
||||
eval ${rcvar}=YES
|
||||
fi
|
||||
|
||||
# $flags from environment overrides ${rcvar}_flags
|
||||
if [ -n "${flags}" ]
|
||||
then
|
||||
eval ${rcvar}_flags="${flags}"
|
||||
fi
|
||||
|
||||
checkyesno()
|
||||
{
|
||||
eval _value=\$${1}
|
||||
case $_value in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0 ;;
|
||||
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1 ;;
|
||||
*)
|
||||
echo "\$${1} is not set properly."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd=${1:-start}
|
||||
case ${cmd} in
|
||||
force*)
|
||||
cmd=${cmd#force}
|
||||
eval ${rcvar}=YES
|
||||
;;
|
||||
esac
|
||||
|
||||
if checkyesno ${rcvar}
|
||||
then
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case ${cmd} in
|
||||
start)
|
||||
if [ -x $D/src ]; then
|
||||
echo "Starting ${name}."
|
||||
cd $D/src
|
||||
ERL_MAX_PORTS=32000 export ERL_MAX_PORTS
|
||||
ulimit -n $ERL_MAX_PORTS
|
||||
su jabber -c "/usr/pkg/bin/erl -sname ejabberd -s ejabberd -heart -detached -sasl sasl_error_logger '{file, \"ejabberd-sasl.log\"}' &" \
|
||||
1>/dev/null 2>&1
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
echo "rpc:call('ejabberd@`hostname -s`', init, stop, [])." | \
|
||||
su jabber -c "/usr/pkg/bin/erl -sname ejabberdstop"
|
||||
;;
|
||||
restart)
|
||||
echo "rpc:call('ejabberd@`hostname -s`', init, restart, [])." | \
|
||||
su jabber -c "/usr/pkg/bin/erl -sname ejabberdrestart"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1,81 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo '1. fetch, compile, and install erlang'
|
||||
|
||||
if [ ! pkg_info erlang 1>/dev/null 2>&1 ]; then
|
||||
cd /usr/pkgsrc/lang/erlang
|
||||
make fetch-list|sh
|
||||
make
|
||||
make install
|
||||
fi
|
||||
if pkg_info erlang | grep -q erlang-9.1nb1; then
|
||||
else
|
||||
echo "erlang-9.1nb1 not installed" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo '2. install crypt_drv.so'
|
||||
|
||||
if [ ! -d /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib ] ; then
|
||||
mkdir -p /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib
|
||||
fi
|
||||
if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/crypto_drv.so ]; then
|
||||
cp work/otp*/lib/crypto/priv/*/*/crypto_drv.so \
|
||||
/usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib
|
||||
fi
|
||||
|
||||
|
||||
echo '3. compile and install elibcrypto.so'
|
||||
|
||||
if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/elibcrypto.so ]; then
|
||||
cd /usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/crypto/c_src
|
||||
ld -r -u CRYPTO_set_mem_functions -u MD5 -u MD5_Init -u MD5_Update \
|
||||
-u MD5_Final -u SHA1 -u SHA1_Init -u SHA1_Update -u SHA1_Final \
|
||||
-u des_set_key -u des_ncbc_encrypt -u des_ede3_cbc_encrypt \
|
||||
-L/usr/lib -lcrypto -o ../priv/obj/i386--netbsdelf/elibcrypto.o
|
||||
cc -shared \
|
||||
-L/usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/erl_interface/obj/i386--netbsdelf \
|
||||
-o ../priv/obj/i386--netbsdelf/elibcrypto.so \
|
||||
../priv/obj/i386--netbsdelf/elibcrypto.o -L/usr/lib -lcrypto
|
||||
cp ../priv/obj/i386--netbsdelf/elibcrypto.so \
|
||||
/usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib
|
||||
fi
|
||||
|
||||
|
||||
echo '4. compile and install ssl_esock'
|
||||
|
||||
if [ ! -f /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/bin/ssl_esock ]; then
|
||||
cd /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/obj/
|
||||
make
|
||||
fi
|
||||
|
||||
|
||||
echo '5. initial ejabberd configuration'
|
||||
|
||||
cd /usr/pkg/jabber/ejabberd/src
|
||||
./configure
|
||||
|
||||
|
||||
echo '6. edit ejabberd Makefiles'
|
||||
|
||||
for M in Makefile mod_*/Makefile; do
|
||||
if [ ! -f $M.orig ]; then
|
||||
mv $M $M.orig
|
||||
sed -e s%/usr/local%/usr/pkg%g < $M.orig > $M
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
echo '7. compile ejabberd'
|
||||
|
||||
gmake
|
||||
for A in mod_irc mod_muc mod_pubsub; do
|
||||
(cd $A; gmake)
|
||||
done
|
||||
|
||||
|
||||
echo ''
|
||||
echo 'now edit ejabberd.cfg'
|
||||
echo ''
|
||||
echo 'to start ejabberd: erl -sname ejabberd -s ejabberd'
|
|
@ -1,66 +0,0 @@
|
|||
% jabber.dbc.mtview.ca.us
|
||||
|
||||
override_acls.
|
||||
|
||||
{acl, admin, {user, "mrose", "jabber.dbc.mtview.ca.us"}}.
|
||||
|
||||
|
||||
{access, announce, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, c2s, [{deny, blocked},
|
||||
{allow, all}]}.
|
||||
{access, c2s_shaper, [{none, admin},
|
||||
{normal, all}]}.
|
||||
{access, configure, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, disco_admin, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, muc_admin, [{allow, admin},
|
||||
{deny, all}]}.
|
||||
{access, register, [{deny, all}]}.
|
||||
{access, s2s_shaper, [{fast, all}]}.
|
||||
|
||||
|
||||
{auth_method, internal}.
|
||||
{host, "jabber.dbc.mtview.ca.us"}.
|
||||
{outgoing_s2s_port, 5269}.
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
{shaper, fast, {maxrate, 50000}}.
|
||||
{welcome_message, none}.
|
||||
|
||||
|
||||
{listen, [{5222, ejabberd_c2s,
|
||||
[{access, c2s},
|
||||
{shaper, c2s_shaper}]},
|
||||
{5223, ejabberd_c2s,
|
||||
[{access, c2s},
|
||||
{shaper, c2s_shaper},
|
||||
{ssl, [{certfile, "/etc/openssl/certs/ejabberd.pem"}]}]},
|
||||
{5269, ejabberd_s2s_in,
|
||||
[{shaper, s2s_shaper}]}]}.
|
||||
|
||||
|
||||
{modules, [
|
||||
{mod_register, []},
|
||||
{mod_roster, []},
|
||||
{mod_privacy, []},
|
||||
{mod_configure, []},
|
||||
{mod_disco, []},
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_echo, [{host, "echo.jabber.dbc.mtview.ca.us"}]},
|
||||
{mod_private, []},
|
||||
% {mod_irc, []},
|
||||
{mod_muc, []},
|
||||
{mod_pubsub, []},
|
||||
{mod_time, []},
|
||||
{mod_last, []},
|
||||
{mod_version, []}
|
||||
]}.
|
||||
|
||||
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
|
@ -1,77 +0,0 @@
|
|||
<!-- aim-transport.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add elogger and rlogger entries when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<format>%d: [%t] (%h): %s</format>
|
||||
<file>/var/log/jabber/aim-transport-error.log</file>
|
||||
</log>
|
||||
|
||||
<log id='rlogger'>
|
||||
<host/>
|
||||
<logtype>record</logtype>
|
||||
<format>%d %h %s</format>
|
||||
<file>/var/log/jabber/aim-transport-record.log</file>
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file.so is loaded in to handle all XDB requests.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file> <!-- This file is part of jabberd-1.4.x. -->
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id='aim.SERVER.COM'>
|
||||
<!-- aim-transport configuration. -->
|
||||
<aimtrans xmlns='jabber:config:aimtrans'>
|
||||
<vCard>
|
||||
<FN>AIM/ICQ Transport</FN>
|
||||
<DESC>This is the AIM/ICQ Transport.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://aim-transport.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<charset>cp1252</charset>
|
||||
</aimtrans>
|
||||
<!-- aim-transport module. -->
|
||||
<load>
|
||||
<aim_transport>/usr/local/lib/jabber/aim-transport.so</aim_transport>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="icq-linker">
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5233</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/aim-transport.pid</pidfile>
|
||||
|
||||
</jabber>
|
|
@ -1,136 +0,0 @@
|
|||
<!-- ile.xml -->
|
||||
|
||||
<config>
|
||||
<jabber>
|
||||
<server>127.0.0.1</server>
|
||||
<port>5238</port>
|
||||
<secret>SECRET</secret>
|
||||
<service>ile.SERVER.COM</service>
|
||||
<connectsleep>7</connectsleep> <!-- seconds to wait if we get disconnected -->
|
||||
<language>en</language>
|
||||
<vCard>
|
||||
<FN>I Love Email</FN>
|
||||
<DESC>With this service you can receive email notifications.
|
||||
|
||||
Security warning: Be careful when using this. Your password will travel in clear from your client to your jabber server if you don't use SSL and it will probably travel in clear from the jabber server to your email server. Use with care. This shouldn't be an issue in your Intranet, but it is if you use an ILE installed in a foreign jabber server.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://ile.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
</jabber>
|
||||
|
||||
<debug>
|
||||
<file>/var/log/jabber/ile.log</file>
|
||||
<level>1</level> <!-- man Net::Jabber::Debug -->
|
||||
</debug>
|
||||
|
||||
<mail>
|
||||
<checkinterval>10</checkinterval> <!-- in minutes -->
|
||||
<timeout>20</timeout> <!-- timeout for IMAP/POP connection, in seconds -->
|
||||
</mail>
|
||||
|
||||
<files>
|
||||
<users>/var/spool/jabber/ile.SERVER.COM/users.db</users>
|
||||
<passwords>/var/spool/jabber/ile.SERVER.COM/passwords.db</passwords>
|
||||
<hosts>/var/spool/jabber/ile.SERVER.COM/hosts.db</hosts>
|
||||
<types>/var/spool/jabber/ile.SERVER.COM/types.db</types>
|
||||
<notifyxa>/var/spool/jabber/ile.SERVER.COM/notifyxa.db</notifyxa>
|
||||
<notifydnd>/var/spool/jabber/ile.SERVER.COM/notifydnd.db</notifydnd>
|
||||
<urls>/var/spool/jabber/ile.SERVER.COM/urls.db</urls>
|
||||
</files>
|
||||
|
||||
<form>
|
||||
<en>
|
||||
<instructions>Please fill in the fields,according to your email account settings and notification preferences</instructions>
|
||||
<title>ILE: Email notification service</title>
|
||||
<email_options>Email account settings</email_options>
|
||||
<user>Username</user>
|
||||
<pass>Password</pass>
|
||||
<host>Hostname</host>
|
||||
<type>Type</type>
|
||||
<newmail>You have received NUM email messages since last time I checked, which was CHECKINTERVAL minutes ago.</newmail>
|
||||
<errorcheck>There was an error while trying to check mail for ACCOUNT.</errorcheck>
|
||||
<notify_options>Notification Options</notify_options>
|
||||
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
|
||||
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Login to ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
|
||||
</en>
|
||||
|
||||
<es>
|
||||
<instructions>Por favor, rellene los campos del formulario.</instructions>
|
||||
<title>ILE: Servicio de notificación de correo</title>
|
||||
<email_options>Configuración de la cuenta de correo</email_options>
|
||||
<user>Usuario</user>
|
||||
<pass>Clave</pass>
|
||||
<host>Host</host>
|
||||
<type>Tipo</type>
|
||||
<newmail>Ha recibido NUM email(s) desde la última comprobación que fue hace CHECKINTERVAL minutos</newmail>
|
||||
<errorcheck>Ha habido un error en la comprobación del correo para la cuenta ACCOUNT.</errorcheck>
|
||||
<notify_options>Opciones de notificación</notify_options>
|
||||
<notifyxa>Notificar incluso si muy ausente (XA)</notifyxa>
|
||||
<notifydnd>Notificar incluso si no molestar (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Leer correo de ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: un notificador de nuevo email - http://ile.jabberstudio.org</iledesc>
|
||||
</es>
|
||||
|
||||
<ca>
|
||||
<instructions>Ompli els camps del formulari.</instructions>
|
||||
<title>ILE: Servei de notificació de nou email</title>
|
||||
<email_options>Dades del compte de mail</email_options>
|
||||
<user>Usuari</user>
|
||||
<pass>Clau</pass>
|
||||
<host>Host</host>
|
||||
<type>Tipus</type>
|
||||
<newmail>Ha rebut NUM email(s) des de la última comprobació que va ser fa CHECKINTERVAL minuts.</newmail>
|
||||
<errorcheck>S'ha produit un error en la comprobació del correu per al compte ACCOUNT.</errorcheck>
|
||||
<notify_options>Opcions de notificació</notify_options>
|
||||
<notifyxa>Notificar si molt absent (XA)</notifyxa>
|
||||
<notifydnd>Notificar si no molestar (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Llegir correu de ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: un notificador de nou email - http://ile.jabberstudio.org</iledesc>
|
||||
</ca>
|
||||
|
||||
<ro>
|
||||
<!-- Contributed by Adrian Rappa -->
|
||||
<instructions>Va rog completati urmatoarele campuri</instructions>
|
||||
<title>I Love Email: new email notification service</title>
|
||||
<email_options>Email account settings</email_options>
|
||||
<user>Nume utilizator</user>
|
||||
<pass>Parola</pass>
|
||||
<host>Nume gazda</host>
|
||||
<type>Tip</type>
|
||||
<newmail>Ati primit NUM mesaj(e) de la ultima verificare, care a fost acum CHECKINTERVAL minute.</newmail>
|
||||
<errorcheck>A fost eroare in timp ce incercam sa verific posta pentru ACCOUNT.</errorcheck>
|
||||
<notify_options>Notification Options</notify_options>
|
||||
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
|
||||
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Login to ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
|
||||
</ro>
|
||||
|
||||
<nl>
|
||||
<!-- Contributed by Sander Devrieze -->
|
||||
<instructions>Vul volgende velden in.</instructions>
|
||||
<title>ILE: Dienst voor e-mailnotificaties</title>
|
||||
<email_options>Instellingen van e-mailaccount</email_options>
|
||||
<user>Gebruikersnaam</user>
|
||||
<pass>Wachtwoord</pass>
|
||||
<host>Inkomende mailserver</host>
|
||||
<type>Type verbinding</type>
|
||||
<newmail>U hebt NUM berichten ontvangen sinds CHECKINTERVAL minuten geleden.</newmail>
|
||||
<errorcheck>Fout tijdens controle op nieuwe e-mails bij ACCOUNT. ILE zal deze account niet meer opnieuw controleren tot u uw registratiegegevens wijzigt of opnieuw aanmeldt.</errorcheck>
|
||||
<notify_options>Notificatie-instellingen</notify_options>
|
||||
<notifyxa>Notificeer ook in de status Niet Beschikbaar (XA)</notifyxa>
|
||||
<notifydnd>Notificeer ook in de status Niet Storen (DND)</notifydnd>
|
||||
<webmail_url>URL van webmail</webmail_url>
|
||||
<webmail_login>Aanmelden op ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: een dienst om e-mailnotificaties te ontvangen: http://ile.jabberstudio.org</iledesc>
|
||||
</nl>
|
||||
|
||||
</form>
|
||||
|
||||
</config>
|
|
@ -1,149 +0,0 @@
|
|||
<jggtrans>
|
||||
|
||||
<service jid="gg.SERVER.COM"/>
|
||||
|
||||
<!-- This connects the jabber-gg-transport process to ejabberd. -->
|
||||
<connect id="gglinker">
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5237</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
|
||||
<register>
|
||||
<!-- This tag contains the message displayed to users at registration time.
|
||||
You can use <p/> and/or <br/> to break lines. Multiple spaces and newlines
|
||||
are converted to just one, so formatting of config file doesn't really matter. -->
|
||||
<instructions>
|
||||
Fill in your GG number (after "username")
|
||||
and password to register on the transport.
|
||||
<p/>To change your information in the GaduGadu directory you need to fill in the other fields.
|
||||
<p/>To remove registration you need to leave the form blank.
|
||||
</instructions>
|
||||
</register>
|
||||
|
||||
<search>
|
||||
<!-- This tag contains the message displayed to users at search time. -->
|
||||
<instructions>
|
||||
To search people:<br/>
|
||||
First fill in surname or family name, nickname, city, birthyear or range of birthyears (eg. 1950-1960)
|
||||
and gender (you may fill in more fields at once).<br/>
|
||||
or<br/>
|
||||
Fill in phone number<br/>
|
||||
or<br/>
|
||||
Fill in the GG number of the person you are searching.
|
||||
</instructions>
|
||||
</search>
|
||||
|
||||
<gateway>
|
||||
<!-- This is message, that may be displayed to user when adding gg contact. -->
|
||||
<desc>Please fill in the GaduGadu number of the person you want to add.</desc>
|
||||
<!-- And this is the prompt for GG number. -->
|
||||
<prompt>GG Nummer</prompt>
|
||||
</gateway>
|
||||
|
||||
<vCard>
|
||||
<FN>Gadu-Gadu Transport</FN>
|
||||
<DESC>This is the Gadu-Gadu Transport.</DESC>
|
||||
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
|
||||
<URL>http://www.jabberstudio.org/projects/jabber-gg-transport/</URL>
|
||||
</vCard>
|
||||
|
||||
<!-- Default user locale (language).
|
||||
Empty means system locale setting,
|
||||
no (or commented-out) <default_locale> tag means no translations. -->
|
||||
<!-- <default_locale>pl_PL</default_locale> -->
|
||||
|
||||
<!-- Logger configuration.
|
||||
You may configure one logger of type "syslog" and/or one of type "file".
|
||||
You may also not configure logging at all. -->
|
||||
<log type="syslog" facility="local0"/>
|
||||
<log type="file">/var/log/jabber/jabber-gg-transport.log</log>
|
||||
|
||||
<!-- Uncomment this, if you want proxy to be used for Gadu-Gadu connection. -->
|
||||
<!--
|
||||
<proxy>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>8080</port>
|
||||
</proxy>
|
||||
-->
|
||||
|
||||
<!-- You can change these values according to your needs. -->
|
||||
<conn_timeout>60</conn_timeout>
|
||||
<ping_interval>10</ping_interval>
|
||||
|
||||
<!-- Gadu-Gadu server doesn't seem to answer pings anymore :-(
|
||||
So let's give it 10 year :-) -->
|
||||
<pong_timeout>315360000</pong_timeout>
|
||||
|
||||
<!-- This time after disconnection from Gadu-Gadu server the transport
|
||||
will try to connect again. -->
|
||||
<reconnect>300</reconnect>
|
||||
|
||||
<!-- How long to wait before restart, after jabber server connection is broken
|
||||
negative value means, that jggtrans should terminate. -->
|
||||
<restart_timeout>60</restart_timeout>
|
||||
|
||||
<!-- Delay between the unavailable presence is received from user and loggin out
|
||||
from Gadu-Gadu - for nice <presence type="invisible"/> support. -->
|
||||
<disconnect_delay>5</disconnect_delay>
|
||||
|
||||
<!-- list of Gadu-Gadu servers to use.
|
||||
<hub/> means "use GG hub to find server"
|
||||
<server/> tag should contain server address and may contain "port"
|
||||
attribute with port number. When TLS is available (supported by libgadu)
|
||||
it will be used unless "tls" attribute is set to "no". Please notice,
|
||||
that not all servers will accept TLS connections.
|
||||
Servers (including hub) are tried in order as they appear in <servers/>
|
||||
element.
|
||||
A reasonable default server list is hardcoded in jggtrans.
|
||||
-->
|
||||
<!--
|
||||
<servers>
|
||||
<hub/>
|
||||
<server port="443">217.17.41.90</server>
|
||||
<server tls="no">217.17.41.85</server>
|
||||
<server tls="no">217.17.41.88</server>
|
||||
</servers>
|
||||
-->
|
||||
|
||||
<!-- Spool directory. This is the place, where user info will be stored. -->
|
||||
<!-- Be careful about permissions - users' Gadu-Gadu passwords are stored there. -->
|
||||
<spool>/var/spool/jabber/gg.SERVER.COM/</spool>
|
||||
|
||||
<!-- Where to store pid file. This tag is optional. -->
|
||||
<pidfile>/var/run/jabber/jabber-gg-transport.pid</pidfile>
|
||||
|
||||
<!-- jid allowed to do some administrative task (eg. discovering online users).
|
||||
May be used multiple times. -->
|
||||
<admin>GG_TRANSPORT_ADMIN@SERVER.COM</admin>
|
||||
|
||||
<!-- ACL gives detailed access control to the transport. -->
|
||||
<acl>
|
||||
<!-- Example entries: -->
|
||||
|
||||
<allow who="admin@SERVER.COM" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
|
||||
<!-- will allow statistics gathering to admin@SERVER.COM -->
|
||||
|
||||
<deny who="*" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
|
||||
<!-- will deny statistics gathering for anybody else -->
|
||||
|
||||
<!-- <allow who="*@SERVER.COM"/> -->
|
||||
<!-- will allow anything else to users from "SERVER.COM" -->
|
||||
|
||||
<!-- <deny what="iq/query?xmlns=jabber:x:register"/> -->
|
||||
<!-- will deny registration for all other users -->
|
||||
|
||||
<!-- <allow what="presence"/> -->
|
||||
<!-- allow presence from anybody -->
|
||||
|
||||
<!-- <allow what="iq"/> -->
|
||||
<!-- allow iq from anybody -->
|
||||
|
||||
<!-- <allow what="message"/> -->
|
||||
<!-- allow message from anybody -->
|
||||
|
||||
<!-- <deny/> -->
|
||||
<!-- will deny anything else -->
|
||||
</acl>
|
||||
|
||||
</jggtrans>
|
|
@ -1,128 +0,0 @@
|
|||
<!-- jit.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add elogger and rlogger entries here when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<file>/var/log/jabber/jit-error</file> <!-- WPJabber logs with date. -->
|
||||
</log>
|
||||
|
||||
<log id='rlogger'>
|
||||
<host/>
|
||||
<logtype>record</logtype>
|
||||
<file>/var/log/jabber/jit-record</file> <!-- WPJabber logs with date. -->
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file-jit.so (the renamed xdb_file.so from WPJabber) is
|
||||
loaded in to handle all XDB requests.
|
||||
Read also the documentation in xdb_file/README from the JIT package.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/xdb_file.so</xdb_file> <!-- The xdb_file.so from WPJabber/JIT. -->
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id="icq.SERVER.COM">
|
||||
<!--
|
||||
Replace SERVER.COM with the same as above to enable sms.
|
||||
-->
|
||||
<host>sms.icq.SERVER.COM</host>
|
||||
<!-- JIT configuration. -->
|
||||
<icqtrans xmlns="jabber:config:icqtrans">
|
||||
<sms>
|
||||
<host>sms.icq.SERVER.COM</host>
|
||||
<!-- Status of virtual "sms-contacts". -->
|
||||
<show>away</show>
|
||||
<status/>
|
||||
</sms>
|
||||
<instructions>Fill in your UIN and password.</instructions>
|
||||
<search>Search ICQ users.</search>
|
||||
<vCard>
|
||||
<FN>ICQ Transport (JIT)</FN>
|
||||
<DESC>This is the Jabber ICQ Transport.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://jit.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<!-- Hashtable for users. -->
|
||||
<prime>3907</prime>
|
||||
<!-- Send messages from ICQ as chat to Jabber clients. -->
|
||||
<chat/>
|
||||
<!-- Enable this for ICQ web presence. -->
|
||||
<web/>
|
||||
<!--
|
||||
If you don't want jabber:x:data forms
|
||||
in reg and search uncomment this tag
|
||||
(Not recomended).
|
||||
-->
|
||||
<no_xdata/>
|
||||
<!--
|
||||
This tag is necessary when using ejabberd.
|
||||
In this way JIT will have its own contact list.
|
||||
-->
|
||||
<own_roster/>
|
||||
<!--
|
||||
When present, this tag will tell JIT not to try to
|
||||
get the user's roster (which will take a bit of time
|
||||
to fail in scenarios described above).
|
||||
-->
|
||||
<no_jabber_roster/>
|
||||
<!-- File with stats. -->
|
||||
<user_count_file>/var/spool/jabber/jit-count</user_count_file>
|
||||
<!--
|
||||
Interval beetween checking sessions: ping, messages, acks.
|
||||
-->
|
||||
<session_check>5</session_check>
|
||||
<!-- Reconnect retries. -->
|
||||
<reconnects>5</reconnects>
|
||||
<!--
|
||||
Time in sec when session can be inactive, 0=disabled.
|
||||
-->
|
||||
<session_timeout>18000</session_timeout>
|
||||
<charset>windows-1252</charset>
|
||||
<server>
|
||||
<host port="5190">login.icq.com</host>
|
||||
</server>
|
||||
</icqtrans>
|
||||
<!-- JIT module. -->
|
||||
<load>
|
||||
<icqtrans>/usr/local/lib/jabber/jit.so</icqtrans>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="icq-linker">
|
||||
<host>SERVER.COM</host>
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5234</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/jit.pid</pidfile>
|
||||
|
||||
</jabber>
|
|
@ -1,118 +0,0 @@
|
|||
<!-- msn-transport.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add elogger and rlogger entries here when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<format>%d: [%t] (%h): %s</format>
|
||||
<file>/var/log/jabber/msn-transport-error.log</file>
|
||||
</log>
|
||||
|
||||
<log id='rlogger'>
|
||||
<host/>
|
||||
<logtype>record</logtype>
|
||||
<format>%d %h %s</format>
|
||||
<file>/var/log/jabber/msn-transport-record.log</file>
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file.so is loaded in to handle all XDB requests.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id="msn.SERVER.COM">
|
||||
<!-- msn-transport configuration. -->
|
||||
<msntrans xmlns="jabber:config:msntrans">
|
||||
<instructions>Fill in your MSN account and password (eg: user1@hotmail.com). A nickname is optional.</instructions>
|
||||
<vCard>
|
||||
<FN>MSN Transport</FN>
|
||||
<DESC>This is the MSN Transport.</DESC>
|
||||
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
|
||||
<URL>http://msn-transport.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<!--
|
||||
Conference support allows you to create groupchat rooms on the
|
||||
msn-transport and invite MSN users to join.
|
||||
-->
|
||||
<conference id="conference.msn.SERVER.COM">
|
||||
<!--
|
||||
This will make MSN transport invite you to a special groupchat
|
||||
room when more then one user joins a normal one-on-one session.
|
||||
Joining this room will make MSN transport "switch" the session
|
||||
into groupchat mode. If you ignore it, MSN transport will
|
||||
continue to send the messages as one-on-one chats.
|
||||
-->
|
||||
<invite>More than one user entered this chat session. Enter this room to switch to groupchat modus.</invite>
|
||||
<notice>
|
||||
<join> is available</join>
|
||||
<leave> has leaved the room</leave>
|
||||
</notice>
|
||||
</conference>
|
||||
<!-- Enable Hotmail inbox notification. -->
|
||||
<headlines/>
|
||||
<!--
|
||||
Enable fancy friendly names
|
||||
If the user enters a nickname upon registration, and the user has
|
||||
a status message, their MSN friendly name will be "nickname - status message".
|
||||
|
||||
If the user does not enter a nickname on registration, but they do have
|
||||
a status message, their friendly name will just be their status message.
|
||||
|
||||
If the user did enter a nickname on registration, but they have a blank status message,
|
||||
then their friendly name will just be the registered nickname.
|
||||
|
||||
If the user did not enter a nickname on registration, and they have a blank status message,
|
||||
their nickname will just be the username portion of their JID.
|
||||
|
||||
If the above chosen friendly name is too long, then it will be truncated and "..." placed
|
||||
at the end. MSN only supports friendly names of 128 characters, so this is unavoidable.
|
||||
|
||||
If this is disabled, then the registered nick is always sent as the MSN friendly name,
|
||||
or if that is blank, the username portion of their JID is sent instead.
|
||||
-->
|
||||
<fancy_friendly/>
|
||||
</msntrans>
|
||||
<!-- msn-transport module. -->
|
||||
<load>
|
||||
<msntrans>/usr/local/lib/jabber/msn-transport.so</msntrans>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="msn-linker">
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5235</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/msn-transport.pid</pidfile>
|
||||
|
||||
</jabber>
|
|
@ -1,86 +0,0 @@
|
|||
<!-- yahoo-transport-2.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add the elogger entry here when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<format>%d: [%t] (%h): %s</format>
|
||||
<file>/var/log/jabber/yahoo-transport-2-error.log</file>
|
||||
<stderr/>
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file.so is loaded in to handle all XDB requests.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id="yahoo.SERVER.COM">
|
||||
<!-- yahoo-transport-2 configuration. -->
|
||||
<config xmlns="jabber:config:yahoo">
|
||||
<vCard>
|
||||
<NAME>Yahoo! Transport</NAME>
|
||||
<FN>vCard not implemented in current version</FN>
|
||||
<DESC>This is the Yahoo! transport.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://yahoo-transport-2.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<instructions>Fill in your YAHOO! Messenger username and password to register on this transport.</instructions>
|
||||
<server>scs.msg.yahoo.com</server>
|
||||
<port>5050</port>
|
||||
<!--
|
||||
The character map. This provides character set translation from UTF-8
|
||||
to the indicated character map. See the man page for 'iconv' for available
|
||||
character maps on your platform. CP1252 is the standard Windows character
|
||||
set.
|
||||
-->
|
||||
<charmap>CP1252</charmap>
|
||||
<!--
|
||||
When this element exists, the transport will send new mail notifications as
|
||||
well as a count of unread messages when the user initially logs in.
|
||||
-->
|
||||
<newmail/>
|
||||
</config>
|
||||
<!-- yahoo-transport-2 module. -->
|
||||
<load>
|
||||
<yahoo_transport>/usr/local/lib/jabber/yahoo-transport-2.so</yahoo_transport>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="yahoo-linker">
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5236</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/yahoo-transport-2.pid</pidfile>
|
||||
|
||||
</jabber>
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# aim-transport -- script to start aim-transport.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jabberd-aim-transport
|
||||
CONF=/etc/jabber/aim-transport.xml
|
||||
NAME=jabberd-aim-transport
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1,43 +0,0 @@
|
|||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# ile -- script to start ILE.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/ile.pl
|
||||
NAME=ile.pl
|
||||
CONF=/etc/jabber/ile.xml
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
echo "Not implemented yet. Starting in normal mode"
|
||||
$0 start
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|status|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1,47 +0,0 @@
|
|||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# jabber-gg-transport -- script to start jabber-gg-transport.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jggtrans
|
||||
CONF=/etc/jabber/jabber-gg-transport.xml
|
||||
NAME=jggtrans
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
rm /var/run/jabber/jabber-gg-transport.pid
|
||||
;;
|
||||
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# jit -- script to start JIT.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/wpjabber-jit
|
||||
CONF=/etc/jabber/jit.xml
|
||||
NAME=wpjabber-jit
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1,50 +0,0 @@
|
|||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# msn-transport -- script to start MSN Transport.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jabberd-msn-transport
|
||||
CONF=/etc/jabber/msn-transport.xml
|
||||
NAME=jabberd-msn-transport
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
strace)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in strace mode."
|
||||
strace -o /opt/ejabberd/var/log/jabber/strace.log $DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/sh
|
||||
##############################################################
|
||||
#
|
||||
# yahoo-transport-2 -- script to start Yahoo-transport-2.
|
||||
#
|
||||
#############################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jabberd-yahoo-transport-2
|
||||
CONF=/etc/jabber/yahoo-transport-2.xml
|
||||
NAME=jabberd-yahoo-transport-2
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#############################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
|
@ -1 +0,0 @@
|
|||
*.beam
|
|
@ -1,109 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
EXPAT_CFLAGS = @EXPAT_CFLAGS@
|
||||
ERLANG_CFLAGS= @ERLANG_CFLAGS@
|
||||
|
||||
EXPAT_LIBS = @EXPAT_LIBS@
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
# make debug=true to compile Erlang module with debug informations.
|
||||
ifdef debug
|
||||
ERLC_FLAGS+=+debug_info
|
||||
endif
|
||||
|
||||
ifdef ejabberd_debug
|
||||
ERLC_FLAGS+=-Dejabberd_debug
|
||||
endif
|
||||
|
||||
ifeq (@roster_gateway_workaround@, true)
|
||||
ERLC_FLAGS+=-DROSTER_GATEWAY_WORKAROUND
|
||||
endif
|
||||
|
||||
prefix = @prefix@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
|
||||
ERLSHLIBS = expat_erl.so
|
||||
SOURCES = $(wildcard *.erl)
|
||||
BEAMS = $(SOURCES:.erl=.beam)
|
||||
|
||||
DESTDIR =
|
||||
|
||||
EJABBERDDIR = $(DESTDIR)@prefix@/var/lib/ejabberd
|
||||
BEAMDIR = $(EJABBERDDIR)/ebin
|
||||
PRIVDIR = $(EJABBERDDIR)/priv
|
||||
SODIR = $(PRIVDIR)/lib
|
||||
MSGSDIR = $(PRIVDIR)/msgs
|
||||
LOGDIR = $(DESTDIR)@prefix@/var/log/ejabberd
|
||||
ETCDIR = $(DESTDIR)@prefix@/etc/ejabberd
|
||||
|
||||
ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress
|
||||
else
|
||||
# Assume Linux-style dynamic library flags
|
||||
DYNAMIC_LIB_CFLAGS = -fpic -shared
|
||||
endif
|
||||
|
||||
all: $(ERLSHLIBS) compile-beam all-recursive
|
||||
|
||||
compile-beam: XmppAddr.hrl $(BEAMS)
|
||||
|
||||
%.beam: %.erl
|
||||
@ERLC@ -W $(ERLC_FLAGS) $<
|
||||
|
||||
|
||||
all-recursive install-recursive uninstall-recursive \
|
||||
clean-recursive distclean-recursive \
|
||||
mostlyclean-recursive maintainer-clean-recursive:
|
||||
@subdirs="$(SUBDIRS)"; for subdir in $$subdirs; do \
|
||||
target=`echo $@|sed 's,-recursive,,'`; \
|
||||
echo making $$target in $$subdir; \
|
||||
(cd $$subdir && $(MAKE) $$target) || exit 1; \
|
||||
done
|
||||
|
||||
|
||||
%.hrl: %.asn1
|
||||
@ERLC@ $(ASN_FLAGS) $<
|
||||
|
||||
$(ERLSHLIBS): %.so: %.c
|
||||
gcc -Wall $(CFLAGS) $(LDFLAGS) $(LIBS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
$(EXPAT_LIBS) $(EXPAT_CFLAGS) \
|
||||
$(ERLANG_LIBS) $(ERLANG_CFLAGS) \
|
||||
-o $@ $(DYNAMIC_LIB_CFLAGS)
|
||||
|
||||
install: all
|
||||
install -d $(BEAMDIR)
|
||||
install -m 644 *.beam $(BEAMDIR)
|
||||
rm -f $(BEAMDIR)/configure.beam
|
||||
install -m 644 *.app $(BEAMDIR)
|
||||
install -d $(SODIR)
|
||||
install -m 644 *.so $(SODIR)
|
||||
install -d $(MSGSDIR)
|
||||
install -m 644 msgs/*.msg $(MSGSDIR)
|
||||
install -d $(ETCDIR)
|
||||
install -b -m 644 ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
|
||||
install -d $(LOGDIR)
|
||||
|
||||
clean: clean-recursive clean-local
|
||||
|
||||
clean-local:
|
||||
rm -f *.beam $(ERLSHLIBS)
|
||||
rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl
|
||||
|
||||
distclean: distclean-recursive clean-local
|
||||
rm -f config.status
|
||||
rm -f config.log
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
Makefile: Makefile.in
|
|
@ -1,163 +0,0 @@
|
|||
|
||||
include Makefile.inc
|
||||
|
||||
ALL : build
|
||||
|
||||
REL=..\release
|
||||
EREL=$(REL)\ejabberd-$(EJABBERD_VERSION)
|
||||
EBIN_DIR=$(EREL)\ebin
|
||||
SRC_DIR=$(EREL)\src
|
||||
PRIV_DIR=$(EREL)\priv
|
||||
SO_DIR=$(EREL)
|
||||
MSGS_DIR=$(EREL)\msgs
|
||||
WIN32_DIR=$(EREL)\win32
|
||||
DOC_DIR=$(EREL)\doc
|
||||
|
||||
NSIS_SCRIPT=win32\ejabberd.nsi
|
||||
NSIS_HEADER=win32\ejabberd.nsh
|
||||
|
||||
installer : $(NSIS_SCRIPT) $(NSIS_HEADER)
|
||||
makensis $(NSIS_SCRIPT)
|
||||
|
||||
$(NSIS_HEADER) : Makefile.inc
|
||||
echo !define OUTFILEDIR "..\$(REL)" >$(NSIS_HEADER)
|
||||
echo !define TESTDIR "..\$(REL)\ejabberd-$(EJABBERD_VERSION)" >>$(NSIS_HEADER)
|
||||
echo !define VERSION "$(EJABBERD_VERSION)" >>$(NSIS_HEADER)
|
||||
|
||||
release_clean :
|
||||
if exist $(REL) rd /s /q $(REL)
|
||||
|
||||
|
||||
release : build release_clean
|
||||
mkdir $(REL)
|
||||
mkdir $(EREL)
|
||||
mkdir $(EBIN_DIR)
|
||||
copy *.beam $(EBIN_DIR)
|
||||
@erase $(EBIN_DIR)\configure.beam
|
||||
copy *.app $(EBIN_DIR)
|
||||
copy *.dll $(SO_DIR)
|
||||
mkdir $(MSGS_DIR)
|
||||
copy msgs\*.msg $(MSGS_DIR)
|
||||
mkdir $(WIN32_DIR)
|
||||
copy win32\ejabberd.cfg $(EREL)
|
||||
copy win32\inetrc $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\libeay32.dll $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\ssleay32.dll $(EREL)
|
||||
copy win32\ejabberd.ico $(WIN32_DIR)
|
||||
mkdir $(SRC_DIR)
|
||||
copy *.app $(SRC_DIR)
|
||||
copy *.erl $(SRC_DIR)
|
||||
copy *.hrl $(SRC_DIR)
|
||||
copy *.c $(SRC_DIR)
|
||||
mkdir $(SRC_DIR)\eldap
|
||||
copy eldap\eldap.* $(SRC_DIR)\eldap
|
||||
copy eldap\ELDAPv3.asn $(SRC_DIR)\eldap
|
||||
mkdir $(SRC_DIR)\mod_irc
|
||||
copy mod_irc\*.erl $(SRC_DIR)\mod_irc
|
||||
copy mod_irc\*.c $(SRC_DIR)\mod_irc
|
||||
mkdir $(SRC_DIR)\mod_muc
|
||||
copy mod_muc\*.erl $(SRC_DIR)\mod_muc
|
||||
mkdir $(SRC_DIR)\mod_pubsub
|
||||
copy mod_pubsub\*.erl $(SRC_DIR)\mod_pubsub
|
||||
mkdir $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.erl $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.c $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.tcl $(SRC_DIR)\stringprep
|
||||
mkdir $(SRC_DIR)\tls
|
||||
copy tls\*.erl $(SRC_DIR)\tls
|
||||
copy tls\*.c $(SRC_DIR)\tls
|
||||
mkdir $(SRC_DIR)\ejabberd_zlib
|
||||
copy ejabberd_zlib\*.erl $(SRC_DIR)\ejabberd_zlib
|
||||
copy ejabberd_zlib\*.c $(SRC_DIR)\ejabberd_zlib
|
||||
mkdir $(SRC_DIR)\web
|
||||
copy web\*.erl $(SRC_DIR)\web
|
||||
mkdir $(SRC_DIR)\odbc
|
||||
copy odbc\*.erl $(SRC_DIR)\odbc
|
||||
copy odbc\*.sql $(EREL)
|
||||
mkdir $(DOC_DIR)
|
||||
copy ..\doc\*.txt $(DOC_DIR)
|
||||
copy ..\doc\*.html $(DOC_DIR)
|
||||
copy ..\doc\*.png $(DOC_DIR)
|
||||
|
||||
SOURCE = expat_erl.c
|
||||
OBJECT = expat_erl.o
|
||||
DLL = expat_erl.dll
|
||||
|
||||
build : $(DLL) compile-beam all-recursive
|
||||
|
||||
all-recursive :
|
||||
cd eldap
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_irc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_muc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_pubsub
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\ejabberd_zlib
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\odbc
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..
|
||||
|
||||
compile-beam : XmppAddr.hrl
|
||||
erl -s make all report -noinput -s erlang halt
|
||||
|
||||
XmppAddr.hrl : XmppAddr.asn1
|
||||
erlc -bber_bin +der +compact_bit_string +optimize +noobj XmppAddr.asn1
|
||||
|
||||
CLEAN : clean-recursive clean-local
|
||||
|
||||
clean-local :
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(DLL)
|
||||
-@erase expat_erl.exp
|
||||
-@erase expat_erl.lib
|
||||
-@erase *.beam
|
||||
-@erase XmppAddr.asn1db
|
||||
-@erase XmppAddr.erl
|
||||
-@erase XmppAddr.hrl
|
||||
|
||||
clean-recursive :
|
||||
cd eldap
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_irc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_muc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_pubsub
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\ejabberd_zlib
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\odbc
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..
|
||||
|
||||
distclean : release_clean clean
|
||||
-@erase $(NSIS_HEADER)
|
||||
-@erase Makefile.inc
|
||||
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT $(EXPAT_FLAG) -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(EXPAT_DIR)\source\lib"
|
||||
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(EXPAT_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
|
||||
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
XmppAddr { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) id-on-xmppAddr(5) }
|
||||
|
||||
DEFINITIONS EXPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) 5 }
|
||||
|
||||
XmppAddr ::= UTF8String
|
||||
|
||||
END
|
|
@ -1,203 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : acl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : ACL support
|
||||
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(acl).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
to_record/3,
|
||||
add/3,
|
||||
add_list/3,
|
||||
match_rule/3,
|
||||
% for debugging only
|
||||
match_acl/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(acl, {aclname, aclspec}).
|
||||
|
||||
start() ->
|
||||
mnesia:create_table(acl,
|
||||
[{disc_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, acl)}]),
|
||||
mnesia:add_table_copy(acl, node(), ram_copies),
|
||||
ok.
|
||||
|
||||
to_record(Host, ACLName, ACLSpec) ->
|
||||
#acl{aclname = {ACLName, Host}, aclspec = ACLSpec}.
|
||||
|
||||
add(Host, ACLName, ACLSpec) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#acl{aclname = {ACLName, Host},
|
||||
aclspec = ACLSpec})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
add_list(Host, ACLs, Clear) ->
|
||||
F = fun() ->
|
||||
if
|
||||
Clear ->
|
||||
Ks = mnesia:select(
|
||||
acl, [{{acl, {'$1', Host}, '$2'}, [], ['$1']}]),
|
||||
lists:foreach(fun(K) ->
|
||||
mnesia:delete({acl, {K, Host}})
|
||||
end, Ks);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(fun(ACL) ->
|
||||
case ACL of
|
||||
#acl{aclname = ACLName,
|
||||
aclspec = ACLSpec} ->
|
||||
mnesia:write(
|
||||
#acl{aclname = {ACLName, Host},
|
||||
aclspec = ACLSpec})
|
||||
end
|
||||
end, ACLs)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, _} ->
|
||||
ok;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
match_rule(global, Rule, JID) ->
|
||||
case Rule of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
_ ->
|
||||
case ejabberd_config:get_global_option({access, Rule, global}) of
|
||||
undefined ->
|
||||
deny;
|
||||
GACLs ->
|
||||
match_acls(GACLs, JID, global)
|
||||
end
|
||||
end;
|
||||
|
||||
match_rule(Host, Rule, JID) ->
|
||||
case Rule of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
_ ->
|
||||
case ejabberd_config:get_global_option({access, Rule, global}) of
|
||||
undefined ->
|
||||
case ejabberd_config:get_global_option({access, Rule, Host}) of
|
||||
undefined ->
|
||||
deny;
|
||||
ACLs ->
|
||||
match_acls(ACLs, JID, Host)
|
||||
end;
|
||||
GACLs ->
|
||||
case ejabberd_config:get_global_option({access, Rule, Host}) of
|
||||
undefined ->
|
||||
match_acls(GACLs, JID, Host);
|
||||
ACLs ->
|
||||
case lists:reverse(GACLs) of
|
||||
[{allow, all} | Rest] ->
|
||||
match_acls(
|
||||
lists:reverse(Rest) ++ ACLs ++
|
||||
[{allow, all}],
|
||||
JID, Host);
|
||||
_ ->
|
||||
match_acls(GACLs ++ ACLs, JID, Host)
|
||||
end
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
match_acls([], _, Host) ->
|
||||
deny;
|
||||
match_acls([{Access, ACL} | ACLs], JID, Host) ->
|
||||
case match_acl(ACL, JID, Host) of
|
||||
true ->
|
||||
Access;
|
||||
_ ->
|
||||
match_acls(ACLs, JID, Host)
|
||||
end.
|
||||
|
||||
match_acl(ACL, JID, Host) ->
|
||||
case ACL of
|
||||
all -> true;
|
||||
none -> false;
|
||||
_ ->
|
||||
{User, Server, Resource} = jlib:jid_tolower(JID),
|
||||
lists:any(fun(#acl{aclspec = Spec}) ->
|
||||
case Spec of
|
||||
all ->
|
||||
true;
|
||||
{user, U} ->
|
||||
(U == User)
|
||||
andalso
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)));
|
||||
{user, U, S} ->
|
||||
(U == User) andalso (S == Server);
|
||||
{server, S} ->
|
||||
S == Server;
|
||||
{user_regexp, UR} ->
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)))
|
||||
andalso is_regexp_match(User, UR);
|
||||
{user_regexp, UR, S} ->
|
||||
(S == Server) andalso
|
||||
is_regexp_match(User, UR);
|
||||
{server_regexp, SR} ->
|
||||
is_regexp_match(Server, SR);
|
||||
{node_regexp, UR, SR} ->
|
||||
is_regexp_match(Server, SR) andalso
|
||||
is_regexp_match(User, UR);
|
||||
{user_glob, UR} ->
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)))
|
||||
andalso
|
||||
is_glob_match(User, UR);
|
||||
{user_glob, UR, S} ->
|
||||
(S == Server) andalso
|
||||
is_glob_match(User, UR);
|
||||
{server_glob, SR} ->
|
||||
is_glob_match(Server, SR);
|
||||
{node_glob, UR, SR} ->
|
||||
is_glob_match(Server, SR) andalso
|
||||
is_glob_match(User, UR);
|
||||
WrongSpec ->
|
||||
?ERROR_MSG(
|
||||
"Wrong ACL expression: ~p~n"
|
||||
"Check your config file and reload it with the override_acls option enabled",
|
||||
[WrongSpec]),
|
||||
false
|
||||
end
|
||||
end,
|
||||
ets:lookup(acl, {ACL, global}) ++
|
||||
ets:lookup(acl, {ACL, Host}))
|
||||
end.
|
||||
|
||||
is_regexp_match(String, RegExp) ->
|
||||
case regexp:first_match(String, RegExp) of
|
||||
nomatch ->
|
||||
false;
|
||||
{match, _, _} ->
|
||||
true;
|
||||
{error, ErrDesc} ->
|
||||
?ERROR_MSG(
|
||||
"Wrong regexp ~p in ACL: ~p",
|
||||
[RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
|
||||
false
|
||||
end.
|
||||
|
||||
is_glob_match(String, Glob) ->
|
||||
is_regexp_match(String, regexp:sh_to_awk(Glob)).
|
||||
|
||||
|
294
ejabberd-1.1.2/src/aclocal.m4
vendored
294
ejabberd-1.1.2/src/aclocal.m4
vendored
|
@ -1,294 +0,0 @@
|
|||
AC_DEFUN(AM_WITH_EXPAT,
|
||||
[ AC_ARG_WITH(expat,
|
||||
[ --with-expat=PREFIX prefix where EXPAT is installed])
|
||||
|
||||
EXPAT_CFLAGS=
|
||||
EXPAT_LIBS=
|
||||
if test x"$with_expat" != x; then
|
||||
EXPAT_CFLAGS="-I$with_expat/include"
|
||||
EXPAT_LIBS="-L$with_expat/lib"
|
||||
fi
|
||||
|
||||
AC_CHECK_LIB(expat, XML_ParserCreate,
|
||||
[ EXPAT_LIBS="$EXPAT_LIBS -lexpat"
|
||||
expat_found=yes ],
|
||||
[ expat_found=no ],
|
||||
"$EXPAT_LIBS")
|
||||
if test $expat_found = no; then
|
||||
AC_MSG_ERROR([Could not find the Expat library])
|
||||
fi
|
||||
expat_save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $EXPAT_CFLAGS"
|
||||
AC_CHECK_HEADERS(expat.h, , expat_found=no)
|
||||
if test $expat_found = no; then
|
||||
AC_MSG_ERROR([Could not find expat.h])
|
||||
fi
|
||||
CFLAGS="$expat_save_CFLAGS"
|
||||
|
||||
AC_SUBST(EXPAT_CFLAGS)
|
||||
AC_SUBST(EXPAT_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN(AM_WITH_ZLIB,
|
||||
[ AC_ARG_WITH(zlib,
|
||||
[ --with-zlib=PREFIX prefix where zlib is installed])
|
||||
|
||||
ZLIB_CFLAGS=
|
||||
ZLIB_LIBS=
|
||||
if test x"$with_zlib" != x; then
|
||||
ZLIB_CFLAGS="-I$with_zlib/include"
|
||||
ZLIB_LIBS="-L$with_zlib/lib"
|
||||
fi
|
||||
|
||||
AC_CHECK_LIB(z, gzgets,
|
||||
[ ZLIB_LIBS="$ZLIB_LIBS -lz"
|
||||
zlib_found=yes ],
|
||||
[ zlib_found=no ],
|
||||
"$ZLIB_LIBS")
|
||||
if test $zlib_found = no; then
|
||||
AC_MSG_ERROR([Could not find the zlib library])
|
||||
fi
|
||||
zlib_save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
|
||||
AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
|
||||
if test $zlib_found = no; then
|
||||
AC_MSG_ERROR([Could not find zlib.h])
|
||||
fi
|
||||
CFLAGS="$zlib_save_CFLAGS"
|
||||
|
||||
AC_SUBST(ZLIB_CFLAGS)
|
||||
AC_SUBST(ZLIB_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN(AM_WITH_ERLANG,
|
||||
[ AC_ARG_WITH(erlang,
|
||||
[ --with-erlang=PREFIX path to erlc and erl ])
|
||||
|
||||
|
||||
AC_PATH_TOOL(ERLC, erlc, , $PATH:$with_erlang:$with_erlang/bin)
|
||||
AC_PATH_TOOL(ERL, erl, , $PATH:$with_erlang:$with_erlang/bin)
|
||||
|
||||
if test "z$ERLC" = "z" || test "z$ERL" = "z"; then
|
||||
AC_MSG_ERROR([erlang not found])
|
||||
fi
|
||||
|
||||
|
||||
cat >>conftest.erl <<_EOF
|
||||
|
||||
-module(conftest).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([[start/0]]).
|
||||
|
||||
start() ->
|
||||
EIDirS = code:lib_dir("erl_interface") ++ "\n",
|
||||
EILibS = libpath("erl_interface") ++ "\n",
|
||||
RootDirS = code:root_dir() ++ "\n",
|
||||
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ RootDirS)),
|
||||
halt().
|
||||
|
||||
%% return physical architecture based on OS/Processor
|
||||
archname() ->
|
||||
ArchStr = erlang:system_info(system_architecture),
|
||||
case os:type() of
|
||||
{win32, _} -> "windows";
|
||||
{unix,UnixName} ->
|
||||
Specs = string:tokens(ArchStr,"-"),
|
||||
Cpu = case lists:nth(2,Specs) of
|
||||
"pc" -> "x86";
|
||||
_ -> hd(Specs)
|
||||
end,
|
||||
atom_to_list(UnixName) ++ "-" ++ Cpu;
|
||||
_ -> "generic"
|
||||
end.
|
||||
|
||||
%% Return arch-based library path or a default value if this directory
|
||||
%% does not exist
|
||||
libpath(App) ->
|
||||
PrivDir = code:priv_dir(App),
|
||||
ArchDir = archname(),
|
||||
LibArchDir = filename:join([[PrivDir,"lib",ArchDir]]),
|
||||
case file:list_dir(LibArchDir) of
|
||||
%% Arch lib dir exists: We use it
|
||||
{ok, _List} -> LibArchDir;
|
||||
%% Arch lib dir does not exist: Return the default value
|
||||
%% ({error, enoent}):
|
||||
_Error -> code:lib_dir("erl_interface") ++ "/lib"
|
||||
end.
|
||||
|
||||
_EOF
|
||||
|
||||
if ! $ERLC conftest.erl; then
|
||||
AC_MSG_ERROR([could not compile sample program])
|
||||
fi
|
||||
|
||||
if ! $ERL -s conftest -noshell; then
|
||||
AC_MSG_ERROR([could not run sample program])
|
||||
fi
|
||||
|
||||
if ! test -f conftest.out; then
|
||||
AC_MSG_ERROR([erlang program was not properly executed, (conftest.out was not produced)])
|
||||
fi
|
||||
|
||||
# First line
|
||||
ERLANG_EI_DIR=`cat conftest.out | head -n 1`
|
||||
# Second line
|
||||
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
|
||||
# Third line
|
||||
ERLANG_DIR=`cat conftest.out | tail -n 1`
|
||||
|
||||
ERLANG_CFLAGS="-I$ERLANG_EI_DIR/include -I$ERLANG_DIR/usr/include"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_LIB -lerl_interface -lei"
|
||||
|
||||
AC_SUBST(ERLANG_CFLAGS)
|
||||
AC_SUBST(ERLANG_LIBS)
|
||||
AC_SUBST(ERLC)
|
||||
AC_SUBST(ERL)
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN(AC_MOD_ENABLE,
|
||||
[
|
||||
$1=
|
||||
make_$1=
|
||||
AC_MSG_CHECKING([whether build $1])
|
||||
AC_ARG_ENABLE($1,
|
||||
[ --enable-$1 enable $1 (default: $2)],
|
||||
[mr_enable_$1="$enableval"],
|
||||
[mr_enable_$1=$2])
|
||||
if test "$mr_enable_$1" = "yes"; then
|
||||
$1=$1
|
||||
make_$1=$1/Makefile
|
||||
fi
|
||||
AC_MSG_RESULT($mr_enable_$1)
|
||||
AC_SUBST($1)
|
||||
AC_SUBST(make_$1)
|
||||
|
||||
])
|
||||
|
||||
|
||||
dnl From Bruno Haible.
|
||||
|
||||
AC_DEFUN([AM_ICONV],
|
||||
[
|
||||
dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
|
||||
dnl those with the standalone portable GNU libiconv installed).
|
||||
AC_ARG_WITH([libiconv-prefix],
|
||||
[ --with-libiconv-prefix=PREFIX prefix where libiconv is installed], [
|
||||
for dir in `echo "$withval" | tr : ' '`; do
|
||||
if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi
|
||||
if test -d $dir/include; then CFLAGS="$CFLAGS -I$dir/include"; fi
|
||||
if test -d $dir/lib; then LDFLAGS="$LDFLAGS -L$dir/lib"; fi
|
||||
done
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
|
||||
am_cv_func_iconv="no, consider installing GNU libiconv"
|
||||
am_cv_lib_iconv=no
|
||||
AC_TRY_LINK([#include <stdlib.h>
|
||||
#include <iconv.h>],
|
||||
[iconv_t cd = iconv_open("","");
|
||||
iconv(cd,NULL,NULL,NULL,NULL);
|
||||
iconv_close(cd);],
|
||||
am_cv_func_iconv=yes)
|
||||
if test "$am_cv_func_iconv" != yes; then
|
||||
am_save_LIBS="$LIBS"
|
||||
LIBS="$LIBS -liconv"
|
||||
AC_TRY_LINK([#include <stdlib.h>
|
||||
#include <iconv.h>],
|
||||
[iconv_t cd = iconv_open("","");
|
||||
iconv(cd,NULL,NULL,NULL,NULL);
|
||||
iconv_close(cd);],
|
||||
am_cv_lib_iconv=yes
|
||||
am_cv_func_iconv=yes)
|
||||
LIBS="$am_save_LIBS"
|
||||
fi
|
||||
dnl trying /usr/local
|
||||
if test "$am_cv_func_iconv" != yes; then
|
||||
am_save_LIBS="$LIBS"
|
||||
am_save_CFLAGS="$CFLAGS"
|
||||
am_save_LDFLAGS="$LDFLAGS"
|
||||
LIBS="$LIBS -liconv"
|
||||
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||
CFLAGS="$CFLAGS -I/usr/local/include"
|
||||
AC_TRY_LINK([#include <stdlib.h>
|
||||
#include <iconv.h>],
|
||||
[iconv_t cd = iconv_open("","");
|
||||
iconv(cd,NULL,NULL,NULL,NULL);
|
||||
iconv_close(cd);],
|
||||
am_cv_lib_iconv=yes
|
||||
am_cv_func_iconv=yes
|
||||
CPPFLAGS="$CPPFLAGS -I/usr/local/include",
|
||||
LDFLAGS="$am_save_LDFLAGS"
|
||||
CFLAGS="$am_save_CFLAGS")
|
||||
LIBS="$am_save_LIBS"
|
||||
fi
|
||||
|
||||
])
|
||||
if test "$am_cv_func_iconv" = yes; then
|
||||
AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
|
||||
AC_MSG_CHECKING([for iconv declaration])
|
||||
AC_CACHE_VAL(am_cv_proto_iconv, [
|
||||
AC_TRY_COMPILE([
|
||||
#include <stdlib.h>
|
||||
#include <iconv.h>
|
||||
extern
|
||||
#ifdef __cplusplus
|
||||
"C"
|
||||
#endif
|
||||
#if defined(__STDC__) || defined(__cplusplus)
|
||||
size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
|
||||
#else
|
||||
size_t iconv();
|
||||
#endif
|
||||
], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
|
||||
am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
|
||||
am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
|
||||
AC_MSG_RESULT([$]{ac_t:-
|
||||
}[$]am_cv_proto_iconv)
|
||||
AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
|
||||
[Define as const if the declaration of iconv() needs const.])
|
||||
fi
|
||||
LIBICONV=
|
||||
if test "$am_cv_lib_iconv" = yes; then
|
||||
LIBICONV="-liconv"
|
||||
fi
|
||||
AC_SUBST(LIBICONV)
|
||||
])
|
||||
|
||||
dnl <openssl>
|
||||
AC_DEFUN(AM_WITH_OPENSSL,
|
||||
[ AC_ARG_WITH(openssl,
|
||||
[ --with-openssl=PREFIX prefix where OPENSSL is installed ])
|
||||
unset SSL_LIBS;
|
||||
unset SSL_CFLAGS;
|
||||
have_openssl=no
|
||||
if test x"$tls" != x; then
|
||||
for ssl_prefix in $withval /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr; do
|
||||
printf "looking for openssl in $ssl_prefix...\n"
|
||||
SSL_CFLAGS="-I$ssl_prefix/include/openssl"
|
||||
SSL_LIBS="-L$ssl_prefix/lib -lcrypto"
|
||||
AC_CHECK_LIB(ssl, SSL_new, [ have_openssl=yes ], [ have_openssl=no ], [ $SSL_LIBS $SSL_CFLAGS ])
|
||||
if test x"$have_openssl" = xyes; then
|
||||
save_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="-I$ssl_prefix/lib $CPPFLAGS"
|
||||
AC_CHECK_HEADERS(openssl/ssl.h, have_openssl_h=yes)
|
||||
CPPFLAGS=$save_CPPFLAGS
|
||||
if test x"$have_openssl_h" = xyes; then
|
||||
have_openssl=yes
|
||||
printf "openssl found in $ssl_prefix\n";
|
||||
SSL_LIBS="-L$ssl_prefix/lib -lssl -lcrypto"
|
||||
CPPFLAGS="-I$ssl_prefix/lib $CPPFLAGS"
|
||||
SSL_CFLAGS="-DHAVE_SSL"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if test x${have_openssl} != xyes; then
|
||||
AC_MSG_ERROR([openssl library cannot be found. Install openssl or disable `tls' module (--disable-tls).])
|
||||
fi
|
||||
AC_SUBST(SSL_LIBS)
|
||||
AC_SUBST(SSL_CFLAGS)
|
||||
fi
|
||||
])
|
||||
dnl <openssl/>
|
|
@ -1,111 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : adhoc.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
|
||||
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(adhoc).
|
||||
-author('henoch@dtek.chalmers.se').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([parse_request/1,
|
||||
produce_response/2,
|
||||
produce_response/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("adhoc.hrl").
|
||||
|
||||
%% Parse an ad-hoc request. Return either an adhoc_request record or
|
||||
%% an {error, ErrorType} tuple.
|
||||
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
|
||||
?DEBUG("entering parse_request...", []),
|
||||
Node = xml:get_tag_attr_s("node", SubEl),
|
||||
SessionID = xml:get_tag_attr_s("sessionid", SubEl),
|
||||
Action = xml:get_tag_attr_s("action", SubEl),
|
||||
XData = find_xdata_el(SubEl),
|
||||
{xmlelement, _, _, AllEls} = SubEl,
|
||||
if XData ->
|
||||
Others = lists:delete(XData, AllEls);
|
||||
true ->
|
||||
Others = AllEls
|
||||
end,
|
||||
|
||||
#adhoc_request{lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID,
|
||||
action = Action,
|
||||
xdata = XData,
|
||||
others = Others};
|
||||
parse_request(_) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
%% Borrowed from mod_vcard.erl
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
||||
find_xdata_el1([]) ->
|
||||
false;
|
||||
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_XDATA ->
|
||||
{xmlelement, Name, Attrs, SubEls};
|
||||
_ ->
|
||||
find_xdata_el1(Els)
|
||||
end;
|
||||
find_xdata_el1([_ | Els]) ->
|
||||
find_xdata_el1(Els).
|
||||
|
||||
%% Produce a <command/> node to use as response from an adhoc_response
|
||||
%% record, filling in values for language, node and session id from
|
||||
%% the request.
|
||||
produce_response(#adhoc_request{lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID},
|
||||
Response) ->
|
||||
produce_response(Response#adhoc_response{lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID}).
|
||||
|
||||
%% Produce a <command/> node to use as response from an adhoc_response
|
||||
%% record.
|
||||
produce_response(#adhoc_response{lang = _Lang,
|
||||
node = Node,
|
||||
sessionid = ProvidedSessionID,
|
||||
status = Status,
|
||||
defaultaction = DefaultAction,
|
||||
actions = Actions,
|
||||
notes = Notes,
|
||||
elements = Elements}) ->
|
||||
SessionID = if is_list(ProvidedSessionID), ProvidedSessionID /= "" ->
|
||||
ProvidedSessionID;
|
||||
true ->
|
||||
jlib:now_to_utc_string(now())
|
||||
end,
|
||||
case Actions of
|
||||
[] ->
|
||||
ActionsEls = [];
|
||||
_ ->
|
||||
case DefaultAction of
|
||||
"" ->
|
||||
ActionsElAttrs = [];
|
||||
_ ->
|
||||
ActionsElAttrs = [{"execute", DefaultAction}]
|
||||
end,
|
||||
ActionsEls = [{xmlelement, "actions",
|
||||
ActionsElAttrs,
|
||||
[{xmlelement, Action, [], []} || Action <- Actions]}]
|
||||
end,
|
||||
NotesEls = lists:map(fun({Type, Text}) ->
|
||||
{xmlelement, "note",
|
||||
[{"type", Type}],
|
||||
[{xmlcdata, Text}]}
|
||||
end, Notes),
|
||||
{xmlelement, "command",
|
||||
[{"xmlns", ?NS_COMMANDS},
|
||||
{"sessionid", SessionID},
|
||||
{"node", Node},
|
||||
{"status", atom_to_list(Status)}],
|
||||
ActionsEls ++ NotesEls ++ Elements}.
|
|
@ -1,15 +0,0 @@
|
|||
-record(adhoc_request, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
action,
|
||||
xdata,
|
||||
others}).
|
||||
|
||||
-record(adhoc_response, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
status,
|
||||
defaultaction = "",
|
||||
actions = [],
|
||||
notes = [],
|
||||
elements = []}).
|
6558
ejabberd-1.1.2/src/configure
vendored
6558
ejabberd-1.1.2/src/configure
vendored
File diff suppressed because it is too large
Load Diff
|
@ -1,85 +0,0 @@
|
|||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.53)
|
||||
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_MAKE_SET
|
||||
|
||||
#locating erlang
|
||||
AM_WITH_ERLANG
|
||||
#locating iconv
|
||||
AM_ICONV
|
||||
#locating libexpat
|
||||
AM_WITH_EXPAT
|
||||
#locating zlib
|
||||
AM_WITH_ZLIB
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
|
||||
# Change default prefix
|
||||
AC_PREFIX_DEFAULT /
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MALLOC
|
||||
AC_HEADER_STDC
|
||||
|
||||
AC_MOD_ENABLE(mod_pubsub, yes)
|
||||
AC_MOD_ENABLE(mod_irc, yes)
|
||||
AC_MOD_ENABLE(mod_muc, yes)
|
||||
AC_MOD_ENABLE(eldap, yes)
|
||||
AC_MOD_ENABLE(web, yes)
|
||||
AC_MOD_ENABLE(tls, yes)
|
||||
AC_MOD_ENABLE(odbc, no)
|
||||
AC_MOD_ENABLE(ejabberd_zlib, yes)
|
||||
|
||||
AC_ARG_ENABLE(roster_gateway_workaround,
|
||||
[ --enable-roster-gateway-workaround Turn on workaround for processing gateway subscriptions (default: no)],
|
||||
[case "${enableval}" in
|
||||
yes) roster_gateway_workaround=true ;;
|
||||
no) roster_gateway_workaround=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-roster-gateway-workaround) ;;
|
||||
esac],[roster_gateway_workaround=false])
|
||||
AC_SUBST(roster_gateway_workaround)
|
||||
|
||||
AC_ARG_ENABLE(mssql,
|
||||
[ --enable-mssql Use Microsoft SQL Server database (default: no, requires --enable-odbc)],
|
||||
[case "${enableval}" in
|
||||
yes) db_type=mssql ;;
|
||||
no) db_type=generic ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;;
|
||||
esac],[db_type=generic])
|
||||
AC_SUBST(db_type)
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
$make_mod_irc
|
||||
$make_mod_muc
|
||||
$make_mod_pubsub
|
||||
$make_eldap
|
||||
$make_web
|
||||
stringprep/Makefile
|
||||
$make_tls
|
||||
$make_odbc
|
||||
$make_ejabberd_zlib])
|
||||
#openssl
|
||||
AM_WITH_OPENSSL
|
||||
# If ssl is kerberized it need krb5.h
|
||||
# On RedHat and OpenBSD, krb5.h is in an unsual place:
|
||||
KRB5_INCLUDE="`krb5-config --cflags 2>/dev/null`"
|
||||
if test -n "$KRB5_INCLUDE" ; then
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_INCLUDE"
|
||||
else
|
||||
# For RedHat For BSD
|
||||
for D in /usr/kerberos/include /usr/include/kerberos /usr/include/kerberosV
|
||||
do
|
||||
if test -d $D ; then
|
||||
CPPFLAGS="$CPPFLAGS -I$D"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
AC_CHECK_HEADER(krb5.h,,)
|
||||
|
||||
AC_OUTPUT
|
|
@ -1,20 +0,0 @@
|
|||
|
||||
@if "x%1"=="x--help" goto usage
|
||||
|
||||
@set arg=dynamic
|
||||
@if "x%1"=="x--static" set arg=static
|
||||
|
||||
@echo Configuring for %arg% build...
|
||||
|
||||
erlc configure.erl
|
||||
erl -s configure -env arg %arg% -noshell
|
||||
|
||||
@goto end
|
||||
|
||||
:usage
|
||||
@echo Usage: configure.bat
|
||||
@echo or configure.bat --static
|
||||
@echo or configure.bat --help
|
||||
|
||||
:end
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : configure.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 27 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(configure).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
start() ->
|
||||
Static = case os:getenv("arg") of
|
||||
false ->
|
||||
false;
|
||||
"static" ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
case Static of
|
||||
true ->
|
||||
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\StaticLibs\\libexpatMT.lib\n",
|
||||
ExpatFlag = "EXPAT_FLAG = -DXML_STATIC\n",
|
||||
IconvDir = "ICONV_DIR = c:\\progra~1\\libiconv-1.9.1-static\n",
|
||||
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n",
|
||||
ZlibDir = "ZLIB_DIR = c:\\progra~1\\zlib-1.2.3\n",
|
||||
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib.lib\n";
|
||||
false ->
|
||||
ExpatLib = "EXPAT_LIB = $(EXPAT_DIR)\\Libs\\libexpat.lib\n",
|
||||
ExpatFlag = "",
|
||||
IconvDir = "ICONV_DIR = c:\\progra~1\\libiconv-1.9.1\n",
|
||||
IconvLib = "ICONV_LIB = $(ICONV_DIR)\\lib\\iconv.lib\n",
|
||||
ZlibDir = "ZLIB_DIR = c:\\progra~1\\zlib-1.2.3\n",
|
||||
ZlibLib = "ZLIB_LIB = $(ZLIB_DIR)\\lib\\zlib1.lib\n"
|
||||
end,
|
||||
|
||||
EVersion = "ERLANG_VERSION = " ++ erlang:system_info(version) ++ "\n",
|
||||
EIDirS = "EI_DIR = " ++ code:lib_dir("erl_interface") ++ "\n",
|
||||
RootDirS = "ERLANG_DIR = " ++ code:root_dir() ++ "\n",
|
||||
Version = "EJABBERD_VERSION = " ++ ?VERSION ++ "\n",
|
||||
ExpatDir = "EXPAT_DIR = c:\\progra~1\\expat-1.95.7\n",
|
||||
OpenSSLDir = "OPENSSL_DIR = c:\\progra~1\\OpenSSL\n",
|
||||
DBType = "DBTYPE = generic\n", %% 'generic' or 'mssql'
|
||||
|
||||
SSLDir = "SSLDIR = " ++ code:lib_dir("ssl") ++ "\n",
|
||||
StdLibDir = "STDLIBDIR = " ++ code:lib_dir("stdlib") ++ "\n",
|
||||
|
||||
file:write_file("Makefile.inc",
|
||||
list_to_binary(EVersion ++
|
||||
EIDirS ++
|
||||
RootDirS ++
|
||||
Version ++
|
||||
SSLDir ++
|
||||
StdLibDir ++
|
||||
OpenSSLDir ++
|
||||
DBType ++
|
||||
ExpatDir ++
|
||||
ExpatLib ++
|
||||
ExpatFlag ++
|
||||
IconvDir ++
|
||||
IconvLib ++
|
||||
ZlibDir ++
|
||||
ZlibLib)),
|
||||
halt().
|
||||
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Cyrus SASL-like library
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
register_mechanism/3,
|
||||
listmech/1,
|
||||
server_new/6,
|
||||
server_start/3,
|
||||
server_step/2]).
|
||||
|
||||
-record(sasl_mechanism, {mechanism, module, require_plain_password}).
|
||||
-record(sasl_state, {service, myname, realm,
|
||||
get_password, check_password,
|
||||
mech_mod, mech_state}).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
behaviour_info(callbacks) ->
|
||||
[{mech_new, 3}, {mech_step, 2}];
|
||||
behaviour_info(Other) ->
|
||||
undefined.
|
||||
|
||||
start() ->
|
||||
ets:new(sasl_mechanism, [named_table,
|
||||
public,
|
||||
{keypos, #sasl_mechanism.mechanism}]),
|
||||
cyrsasl_plain:start([]),
|
||||
cyrsasl_digest:start([]),
|
||||
cyrsasl_anonymous:start([]),
|
||||
ok.
|
||||
|
||||
register_mechanism(Mechanism, Module, RequirePlainPassword) ->
|
||||
ets:insert(sasl_mechanism,
|
||||
#sasl_mechanism{mechanism = Mechanism,
|
||||
module = Module,
|
||||
require_plain_password = RequirePlainPassword}).
|
||||
|
||||
% TODO: use callbacks
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
check_authzid(State, Props) ->
|
||||
AuthzId = xml:get_attr_s(authzid, Props),
|
||||
case jlib:string_to_jid(AuthzId) of
|
||||
error ->
|
||||
{error, "invalid-authzid"};
|
||||
JID ->
|
||||
LUser = jlib:nodeprep(xml:get_attr_s(username, Props)),
|
||||
{U, S, R} = jlib:jid_tolower(JID),
|
||||
case R of
|
||||
"" ->
|
||||
{error, "invalid-authzid"};
|
||||
_ ->
|
||||
case {LUser, ?MYNAME} of
|
||||
{U, S} ->
|
||||
ok;
|
||||
_ ->
|
||||
{error, "invalid-authzid"}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
check_credentials(State, Props) ->
|
||||
User = xml:get_attr_s(username, Props),
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, "not-authorized"};
|
||||
"" ->
|
||||
{error, "not-authorized"};
|
||||
LUser ->
|
||||
ok
|
||||
end.
|
||||
|
||||
listmech(Host) ->
|
||||
RequirePlainPassword = ejabberd_auth:plain_password_required(Host),
|
||||
|
||||
Mechs = ets:select(sasl_mechanism,
|
||||
[{#sasl_mechanism{mechanism = '$1',
|
||||
require_plain_password = '$2',
|
||||
_ = '_'},
|
||||
if
|
||||
RequirePlainPassword ->
|
||||
[{'==', '$2', false}];
|
||||
true ->
|
||||
[]
|
||||
end,
|
||||
['$1']}]),
|
||||
filter_anonymous(Host, Mechs).
|
||||
|
||||
server_new(Service, ServerFQDN, UserRealm, SecFlags,
|
||||
GetPassword, CheckPassword) ->
|
||||
#sasl_state{service = Service,
|
||||
myname = ServerFQDN,
|
||||
realm = UserRealm,
|
||||
get_password = GetPassword,
|
||||
check_password = CheckPassword}.
|
||||
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case lists:member(Mech, listmech(State#sasl_state.myname)) of
|
||||
true ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Module}] ->
|
||||
{ok, MechState} = Module:mech_new(
|
||||
State#sasl_state.myname,
|
||||
State#sasl_state.get_password,
|
||||
State#sasl_state.check_password),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
_ ->
|
||||
{error, "no-mechanism"}
|
||||
end;
|
||||
false ->
|
||||
{error, "no-mechanism"}
|
||||
end.
|
||||
|
||||
server_step(State, ClientIn) ->
|
||||
Module = State#sasl_state.mech_mod,
|
||||
MechState = State#sasl_state.mech_state,
|
||||
case Module:mech_step(MechState, ClientIn) of
|
||||
{ok, Props} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok ->
|
||||
{ok, Props};
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
end;
|
||||
{continue, ServerOut, NewMechState} ->
|
||||
{continue, ServerOut,
|
||||
State#sasl_state{mech_state = NewMechState}};
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
%% Remove the anonymous mechanism from the list if not enabled for the given
|
||||
%% host
|
||||
filter_anonymous(Host, Mechs) ->
|
||||
case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of
|
||||
true -> Mechs;
|
||||
false -> Mechs -- ["ANONYMOUS"]
|
||||
end.
|
|
@ -1,39 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_anonymous.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : ANONYMOUS SASL mechanism
|
||||
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt
|
||||
|
||||
-module(cyrsasl_anonymous).
|
||||
-vsn('$Revision$').
|
||||
|
||||
-export([start/1, stop/0, mech_new/3, mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {server}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword) ->
|
||||
{ok, #state{server = Host}}.
|
||||
|
||||
mech_step(State, _ClientIn) ->
|
||||
%% We generate a random username:
|
||||
User = lists:concat([randoms:get_string() | tuple_to_list(now())]),
|
||||
Server = State#state.server,
|
||||
|
||||
%% Checks that the username is available
|
||||
case ejabberd_auth:is_user_exists(User, Server) of
|
||||
true -> {error, "not-authorized"};
|
||||
false -> {ok, [{username, User}]}
|
||||
end.
|
|
@ -1,163 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_digest.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : DIGEST-MD5 SASL mechanism
|
||||
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_digest).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
mech_new/3,
|
||||
mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {step, nonce, username, authzid, get_password}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(_Host, GetPassword, _CheckPassword) ->
|
||||
{ok, #state{step = 1,
|
||||
nonce = randoms:get_string(),
|
||||
get_password = GetPassword}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
"nonce=\"" ++ Nonce ++
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
|
||||
State#state{step = 3}};
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
bad ->
|
||||
{error, "bad-protocol"};
|
||||
KeyVals ->
|
||||
UserName = xml:get_attr_s("username", KeyVals),
|
||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||
case (State#state.get_password)(UserName) of
|
||||
false ->
|
||||
{error, "not-authorized"};
|
||||
Passwd ->
|
||||
Response = response(KeyVals, UserName, Passwd,
|
||||
Nonce, AuthzId, "AUTHENTICATE"),
|
||||
case xml:get_attr_s("response", KeyVals) of
|
||||
Response ->
|
||||
RspAuth = response(KeyVals,
|
||||
UserName, Passwd,
|
||||
Nonce, AuthzId, ""),
|
||||
{continue,
|
||||
"rspauth=" ++ RspAuth,
|
||||
State#state{step = 5,
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
_ ->
|
||||
{error, "not-authorized"}
|
||||
end
|
||||
end
|
||||
end;
|
||||
mech_step(#state{step = 5,
|
||||
username = UserName,
|
||||
authzid = AuthzId}, "") ->
|
||||
{ok, [{username, UserName}, {authzid, AuthzId}]};
|
||||
mech_step(A, B) ->
|
||||
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||
{error, "bad-protocol"}.
|
||||
|
||||
|
||||
parse(S) ->
|
||||
parse1(S, "", []).
|
||||
|
||||
parse1([$= | Cs], S, Ts) ->
|
||||
parse2(Cs, lists:reverse(S), "", Ts);
|
||||
parse1([$, | Cs], [], Ts) ->
|
||||
parse1(Cs, [], Ts);
|
||||
parse1([$\s | Cs], [], Ts) ->
|
||||
parse1(Cs, [], Ts);
|
||||
parse1([C | Cs], S, Ts) ->
|
||||
parse1(Cs, [C | S], Ts);
|
||||
parse1([], [], T) ->
|
||||
lists:reverse(T);
|
||||
parse1([], _S, _T) ->
|
||||
bad.
|
||||
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, Val, Ts);
|
||||
parse2([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse2([], _, _, _) ->
|
||||
bad.
|
||||
|
||||
parse3([$" | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse3([C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([], _, _, _) ->
|
||||
bad.
|
||||
|
||||
parse4([$, | Cs], Key, Val, Ts) ->
|
||||
parse1(Cs, "", [{Key, lists:reverse(Val)} | Ts]);
|
||||
parse4([$\s | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse4([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse4([], Key, Val, Ts) ->
|
||||
parse1([], "", [{Key, lists:reverse(Val)} | Ts]).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
digit_to_xchar(D) when (D >= 0) and (D < 10) ->
|
||||
D + 48;
|
||||
digit_to_xchar(D) ->
|
||||
D + 87.
|
||||
|
||||
hex(S) ->
|
||||
hex(S, []).
|
||||
|
||||
hex([], Res) ->
|
||||
lists:reverse(Res);
|
||||
hex([N | Ns], Res) ->
|
||||
hex(Ns, [digit_to_xchar(N rem 16),
|
||||
digit_to_xchar(N div 16) | Res]).
|
||||
|
||||
|
||||
response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
|
||||
Realm = xml:get_attr_s("realm", KeyVals),
|
||||
CNonce = xml:get_attr_s("cnonce", KeyVals),
|
||||
DigestURI = xml:get_attr_s("digest-uri", KeyVals),
|
||||
NC = xml:get_attr_s("nc", KeyVals),
|
||||
QOP = xml:get_attr_s("qop", KeyVals),
|
||||
A1 = case AuthzId of
|
||||
"" ->
|
||||
binary_to_list(
|
||||
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
|
||||
":" ++ Nonce ++ ":" ++ CNonce;
|
||||
_ ->
|
||||
binary_to_list(
|
||||
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
|
||||
":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
|
||||
end,
|
||||
A2 = case QOP of
|
||||
"auth" ->
|
||||
A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
T = hex(binary_to_list(crypto:md5(A1))) ++ ":" ++ Nonce ++ ":" ++
|
||||
NC ++ ":" ++ CNonce ++ ":" ++ QOP ++ ":" ++
|
||||
hex(binary_to_list(crypto:md5(A2))),
|
||||
hex(binary_to_list(crypto:md5(T))).
|
||||
|
||||
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_plain.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : PLAIN SASL mechanism
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_plain).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1, stop/0, mech_new/3, mech_step/2, parse/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {check_password}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("PLAIN", ?MODULE, false),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(_Host, _GetPassword, CheckPassword) ->
|
||||
{ok, #state{check_password = CheckPassword}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[AuthzId, User, Password] ->
|
||||
case (State#state.check_password)(User, Password) of
|
||||
true ->
|
||||
{ok, [{username, User}, {authzid, AuthzId}]};
|
||||
_ ->
|
||||
{error, "bad-auth"}
|
||||
end;
|
||||
_ ->
|
||||
{error, "bad-protocol"}
|
||||
end.
|
||||
|
||||
|
||||
parse(S) ->
|
||||
parse1(S, "", []).
|
||||
|
||||
parse1([0 | Cs], S, T) ->
|
||||
parse1(Cs, "", [lists:reverse(S) | T]);
|
||||
parse1([C | Cs], S, T) ->
|
||||
parse1(Cs, [C | S], T);
|
||||
%parse1([], [], T) ->
|
||||
% lists:reverse(T);
|
||||
parse1([], S, T) ->
|
||||
lists:reverse([lists:reverse(S) | T]).
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
% $Id$
|
||||
|
||||
{application, ejabberd,
|
||||
[{description, "ejabberd"},
|
||||
{vsn, "1.1.2"},
|
||||
{modules, [acl,
|
||||
configure,
|
||||
cyrsasl,
|
||||
cyrsasl_digest,
|
||||
cyrsasl_plain,
|
||||
ejabberd,
|
||||
ejabberd_app,
|
||||
ejabberd_auth,
|
||||
ejabberd_c2s,
|
||||
ejabberd_config,
|
||||
ejabberd_listener,
|
||||
ejabberd_logger_h,
|
||||
ejabberd_local,
|
||||
ejabberd_router,
|
||||
ejabberd_s2s,
|
||||
ejabberd_s2s_in,
|
||||
ejabberd_s2s_out,
|
||||
ejabberd_service,
|
||||
ejabberd_sm,
|
||||
ejabberd_sup,
|
||||
ejabberd_tmp_sup,
|
||||
gen_iq_handler,
|
||||
gen_mod,
|
||||
jd2ejd,
|
||||
jlib,
|
||||
mod_configure,
|
||||
mod_disco,
|
||||
mod_echo,
|
||||
mod_last,
|
||||
mod_offline,
|
||||
mod_private,
|
||||
mod_register,
|
||||
mod_roster,
|
||||
mod_stats,
|
||||
mod_time,
|
||||
mod_vcard,
|
||||
mod_version,
|
||||
randoms,
|
||||
sha,
|
||||
shaper,
|
||||
translate,
|
||||
xml,
|
||||
xml_stream
|
||||
]},
|
||||
{registered, [ejabberd,
|
||||
ejabberd_sup,
|
||||
ejabberd_auth,
|
||||
ejabberd_router,
|
||||
ejabberd_sm,
|
||||
ejabberd_s2s,
|
||||
ejabberd_local,
|
||||
ejabberd_listeners,
|
||||
ejabberd_iq_sup,
|
||||
ejabberd_service_sup,
|
||||
ejabberd_s2s_out_sup,
|
||||
ejabberd_s2s_in_sup,
|
||||
ejabberd_c2s_sup,
|
||||
ejabberd_mod_roster,
|
||||
ejabberd_mod_echo,
|
||||
ejabberd_mod_pubsub,
|
||||
ejabberd_mod_irc,
|
||||
ejabberd_mod_muc,
|
||||
ejabberd_offline,
|
||||
random_generator
|
||||
]},
|
||||
{applications, [kernel, stdlib]},
|
||||
{env, []},
|
||||
{mod, {ejabberd_app, []}}]}.
|
||||
|
||||
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
|
@ -1,187 +0,0 @@
|
|||
% $Id$
|
||||
|
||||
%override_acls.
|
||||
|
||||
|
||||
% Users that have admin access. Add line like one of the following after you
|
||||
% will be successfully registered on server to get admin access:
|
||||
%{acl, admin, {user, "aleksey"}}.
|
||||
%{acl, admin, {user, "ermine"}}.
|
||||
|
||||
% Blocked users:
|
||||
%{acl, blocked, {user, "test"}}.
|
||||
|
||||
% Local users:
|
||||
{acl, local, {user_regexp, ""}}.
|
||||
|
||||
% Another examples of ACLs:
|
||||
%{acl, jabberorg, {server, "jabber.org"}}.
|
||||
%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
|
||||
%{acl, test, {user_regexp, "^test"}}.
|
||||
%{acl, test, {user_glob, "test*"}}.
|
||||
|
||||
% Everybody can create pubsub nodes
|
||||
{access, pubsub_createnode, [{allow, all}]}.
|
||||
|
||||
% Only admins can use configuration interface:
|
||||
{access, configure, [{allow, admin}]}.
|
||||
|
||||
% Every username can be registered via in-band registration:
|
||||
% You could replace {allow, all} with {deny, all} to prevent user from using
|
||||
% in-band registration
|
||||
{access, register, [{allow, all}]}.
|
||||
|
||||
% After successful registration user will get message with following subject
|
||||
% and body:
|
||||
{welcome_message,
|
||||
{"Welcome!",
|
||||
"Welcome to Jabber Service. "
|
||||
"For information about Jabber visit http://jabber.org"}}.
|
||||
% Replace them with 'none' if you don't want to send such message:
|
||||
%{welcome_message, none}.
|
||||
|
||||
% List of people who will get notifications about registered users
|
||||
%{registration_watchers, ["admin1@localhost",
|
||||
% "admin2@localhost"]}.
|
||||
|
||||
% Only admins can send announcement messages:
|
||||
{access, announce, [{allow, admin}]}.
|
||||
|
||||
|
||||
% Only non-blocked users can use c2s connections:
|
||||
{access, c2s, [{deny, blocked},
|
||||
{allow, all}]}.
|
||||
|
||||
% Set shaper with name "normal" to limit traffic speed to 1000B/s
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
|
||||
% Set shaper with name "fast" to limit traffic speed to 50000B/s
|
||||
{shaper, fast, {maxrate, 50000}}.
|
||||
|
||||
% For all users except admins used "normal" shaper
|
||||
{access, c2s_shaper, [{none, admin},
|
||||
{normal, all}]}.
|
||||
|
||||
% For all S2S connections used "fast" shaper
|
||||
{access, s2s_shaper, [{fast, all}]}.
|
||||
|
||||
% Admins of this server are also admins of MUC service:
|
||||
{access, muc_admin, [{allow, admin}]}.
|
||||
|
||||
% All users are allowed to use MUC service:
|
||||
{access, muc, [{allow, all}]}.
|
||||
|
||||
% This rule allows access only for local users:
|
||||
{access, local, [{allow, local}]}.
|
||||
|
||||
|
||||
% Authentication method. If you want to use internal user base, then use
|
||||
% this line:
|
||||
{auth_method, internal}.
|
||||
|
||||
% For LDAP authentication use these lines instead of above one:
|
||||
%{auth_method, ldap}.
|
||||
%{ldap_servers, ["localhost"]}. % List of LDAP servers
|
||||
%{ldap_uidattr, "uid"}. % LDAP attribute that holds user ID
|
||||
%{ldap_base, "dc=example,dc=com"}. % Search base of LDAP directory
|
||||
%{ldap_rootdn, "dc=example,dc=com"}. % LDAP manager
|
||||
%{ldap_password, "******"}. % Password to LDAP manager
|
||||
|
||||
% For authentication via external script use the following:
|
||||
%{auth_method, external}.
|
||||
%{extauth_program, "/path/to/authentication/script"}.
|
||||
|
||||
% For authentication via ODBC use the following:
|
||||
%{auth_method, odbc}.
|
||||
%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
|
||||
|
||||
|
||||
% Host name:
|
||||
{hosts, ["localhost"]}.
|
||||
|
||||
%% Define the maximum number of time a single user is allowed to connect:
|
||||
{max_user_sessions, 10}.
|
||||
|
||||
%% Anonymous login support:
|
||||
%% auth_method: anonymous
|
||||
%% anonymous_protocol: sasl_anon|login_anon|both
|
||||
%% allow_multiple_connections: true|false
|
||||
%%{host_config, "public.example.org", [{auth_method, anonymous},
|
||||
%% {allow_multiple_connections, false},
|
||||
%% {anonymous_protocol, sasl_anon}]}.
|
||||
%% To use both anonymous and internal authentication:
|
||||
%%{host_config, "public.example.org", [{auth_method, [anonymous, internal]}]}.
|
||||
|
||||
% Default language for server messages
|
||||
{language, "en"}.
|
||||
|
||||
% Listened ports:
|
||||
{listen,
|
||||
[{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper},
|
||||
{max_stanza_size, 65536},
|
||||
starttls, {certfile, "./ssl.pem"}]},
|
||||
{5223, ejabberd_c2s, [{access, c2s},
|
||||
{max_stanza_size, 65536},
|
||||
tls, {certfile, "./ssl.pem"}]},
|
||||
% Use these two lines instead if TLS support is not compiled
|
||||
%{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]},
|
||||
%{5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
|
||||
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper},
|
||||
{max_stanza_size, 131072}
|
||||
]},
|
||||
{5280, ejabberd_http, [http_poll, web_admin]},
|
||||
{8888, ejabberd_service, [{access, all},
|
||||
{hosts, ["icq.localhost", "sms.localhost"],
|
||||
[{password, "secret"}]}]}
|
||||
]}.
|
||||
|
||||
|
||||
% Use STARTTLS+Dialback for S2S connections
|
||||
{s2s_use_starttls, true}.
|
||||
{s2s_certfile, "./ssl.pem"}.
|
||||
%{domain_certfile, "example.org", "./example_org.pem"}.
|
||||
%{domain_certfile, "example.com", "./example_com.pem"}.
|
||||
|
||||
% If SRV lookup fails, then port 5269 is used to communicate with remote server
|
||||
{outgoing_s2s_port, 5269}.
|
||||
|
||||
|
||||
% Used modules:
|
||||
{modules,
|
||||
[
|
||||
{mod_register, [{access, register}]},
|
||||
{mod_roster, []},
|
||||
{mod_privacy, []},
|
||||
{mod_adhoc, []},
|
||||
{mod_configure, []}, % Depends on mod_adhoc
|
||||
{mod_configure2, []},
|
||||
{mod_disco, []},
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_announce, [{access, announce}]}, % Depends on mod_adhoc
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
% Default options for mod_muc:
|
||||
% host: "conference." ++ ?MYNAME
|
||||
% access: all
|
||||
% access_create: all
|
||||
% access_admin: none (only room creator has owner privileges)
|
||||
{mod_muc, [{access, muc},
|
||||
{access_create, muc},
|
||||
{access_admin, muc_admin}]},
|
||||
% {mod_muc_log, []},
|
||||
% {mod_shared_roster, []},
|
||||
{mod_pubsub, [{access_createnode, pubsub_createnode}]},
|
||||
{mod_time, []},
|
||||
{mod_last, []},
|
||||
{mod_version, []}
|
||||
]}.
|
||||
|
||||
|
||||
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
|
@ -1,34 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0, stop/0,
|
||||
get_so_path/0]).
|
||||
|
||||
start() ->
|
||||
application:start(ejabberd).
|
||||
|
||||
stop() ->
|
||||
application:stop(ejabberd).
|
||||
|
||||
|
||||
get_so_path() ->
|
||||
case os:getenv("EJABBERD_SO_PATH") of
|
||||
false ->
|
||||
case code:priv_dir(ejabberd) of
|
||||
{error, _} ->
|
||||
".";
|
||||
Path ->
|
||||
filename:join([Path, "lib"])
|
||||
end;
|
||||
Path ->
|
||||
Path
|
||||
end.
|
|
@ -1,41 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd.hrl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 17 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(VERSION, "1.1.2").
|
||||
|
||||
%-define(ejabberd_debug, true).
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(ejabberd_debug).
|
||||
-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
-else.
|
||||
-define(DEBUG(F,A),[]).
|
||||
-endif.
|
||||
|
||||
-define(ERROR_MSG(Format, Args),
|
||||
error_logger:error_msg("E(~p:~p:~p): "++Format++"~n",
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
|
||||
-define(INFO_MSG(Format, Args),
|
||||
error_logger:info_msg("I(~p:~p:~p): "++Format++"~n",
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
|
||||
|
||||
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
|
||||
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
|
||||
-define(S2STIMEOUT, 600000).
|
||||
-define(MYLANG, ejabberd_config:get_global_option(language)).
|
||||
|
||||
-define(MSGS_DIR, "msgs").
|
||||
-define(CONFIG_PATH, "ejabberd.cfg").
|
||||
-define(LOG_PATH, "ejabberd.log").
|
||||
|
||||
|
||||
-define(PRIVACY_SUPPORT, true).
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
%%%-------------------------------------------------------------------
|
||||
%%% File : ejabberd_admin.erl
|
||||
%%% Author : Mickael Remond <mremond@process-one.net>
|
||||
%%% Description : This module gathers admin functions used by different
|
||||
%%% access method:
|
||||
%%% - ejabberdctl command-line tool
|
||||
%%% - web admin interface
|
||||
%%% - adhoc mode
|
||||
%%%
|
||||
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%% Id : $Id: $
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Copyright (c) 2006 Process One
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_admin).
|
||||
-author('mickael.remond@process-one.net').
|
||||
-vsn('$Revision: $ ').
|
||||
|
||||
-export([restore/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%% Mnesia database restore
|
||||
%% This function is called from ejabberd_ctl, ejabberd_web_admin and
|
||||
%% mod_configure/adhoc
|
||||
restore(Path) ->
|
||||
mnesia:restore(Path, [{keep_tables,keep_tables()},
|
||||
{default_op, skip_tables}]).
|
||||
|
||||
%% This function return a list of tables that should be kept from a previous
|
||||
%% version backup.
|
||||
%% Obsolete tables or tables created by module who are no longer used are not
|
||||
%% restored and are ignored.
|
||||
keep_tables() ->
|
||||
lists:flatten([acl, passwd, config, local_config, disco_publish,
|
||||
keep_modules_tables()]).
|
||||
|
||||
%% Returns the list of modules tables in use, according to the list of actually
|
||||
%% loaded modules
|
||||
keep_modules_tables() ->
|
||||
lists:map(fun(Module) -> module_tables(Module) end,
|
||||
gen_mod:loaded_modules(?MYNAME)).
|
||||
|
||||
%% TODO: This mapping should probably be moved to a callback function in each
|
||||
%% module.
|
||||
%% Mapping between modules and their tables
|
||||
module_tables(mod_announce) -> [motd, motd_users];
|
||||
module_tables(mod_irc) -> [irc_custom];
|
||||
module_tables(mod_last) -> [last_activity];
|
||||
module_tables(mod_muc) -> [muc_room, muc_registered];
|
||||
module_tables(mod_offline) -> [offline_msg];
|
||||
module_tables(mod_privacy) -> [privacy];
|
||||
module_tables(mod_private) -> [private_storage];
|
||||
module_tables(mod_pubsub) -> [pubsub_node];
|
||||
module_tables(mod_roster) -> [roster];
|
||||
module_tables(mod_shared_roster) -> [sr_group, sr_user];
|
||||
module_tables(mod_vcard) -> [vcard, vcard_search];
|
||||
module_tables(_Other) -> [].
|
|
@ -1,104 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_app.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_app).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
-export([start/2, stop/1, init/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
start(normal, _Args) ->
|
||||
application:start(sasl),
|
||||
randoms:start(),
|
||||
db_init(),
|
||||
sha:start(),
|
||||
catch ssl:start(),
|
||||
translate:start(),
|
||||
acl:start(),
|
||||
ejabberd_ctl:init(),
|
||||
gen_mod:start(),
|
||||
ejabberd_config:start(),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
ejabberd_auth:start(),
|
||||
cyrsasl:start(),
|
||||
% Profiling
|
||||
%eprof:start(),
|
||||
%eprof:profile([self()]),
|
||||
%fprof:trace(start, "/tmp/fprof"),
|
||||
start(),
|
||||
load_modules(),
|
||||
Sup;
|
||||
start(_, _) ->
|
||||
{error, badarg}.
|
||||
|
||||
stop(_StartArgs) ->
|
||||
ok.
|
||||
|
||||
start() ->
|
||||
spawn_link(?MODULE, init, []).
|
||||
|
||||
init() ->
|
||||
register(ejabberd, self()),
|
||||
%erlang:system_flag(fullsweep_after, 0),
|
||||
%error_logger:logfile({open, ?LOG_PATH}),
|
||||
LogPath =
|
||||
case application:get_env(log_path) of
|
||||
{ok, Path} ->
|
||||
Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false ->
|
||||
?LOG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
end,
|
||||
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
|
||||
erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||
case erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl) of
|
||||
ok -> ok;
|
||||
{error, already_loaded} -> ok
|
||||
end,
|
||||
Port = open_port({spawn, expat_erl}, [binary]),
|
||||
loop(Port).
|
||||
|
||||
|
||||
loop(Port) ->
|
||||
receive
|
||||
_ ->
|
||||
loop(Port)
|
||||
end.
|
||||
|
||||
db_init() ->
|
||||
case mnesia:system_info(extra_db_nodes) of
|
||||
[] ->
|
||||
mnesia:create_schema([node()]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
mnesia:start(),
|
||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||
|
||||
load_modules() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
case ejabberd_config:get_local_option({modules, Host}) of
|
||||
undefined ->
|
||||
ok;
|
||||
Modules ->
|
||||
lists:foreach(
|
||||
fun({Module, Args}) ->
|
||||
gen_mod:start_module(Host, Module, Args)
|
||||
end, Modules)
|
||||
end
|
||||
end, ?MYHOSTS).
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification
|
||||
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Updated : 23 Feb 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%% for anonymous login support
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% TODO: Use the functions in ejabberd auth to add and remove users.
|
||||
|
||||
-module(ejabberd_auth).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
is_user_exists_in_other_modules/3,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/1,
|
||||
ctl_process_get_registered/3
|
||||
]).
|
||||
|
||||
-export([auth_modules/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_ctl.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
lists:foreach(
|
||||
fun(M) ->
|
||||
M:start(Host)
|
||||
end, auth_modules(Host))
|
||||
end, ?MYHOSTS).
|
||||
|
||||
plain_password_required(Server) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:plain_password_required()
|
||||
end, auth_modules(Server)).
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:check_password(User, Server, Password)
|
||||
end, auth_modules(Server)).
|
||||
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:check_password(User, Server, Password, StreamID, Digest)
|
||||
end, auth_modules(Server)).
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
lists:foldl(
|
||||
fun(M, {error, _}) ->
|
||||
M:set_password(User, Server, Password);
|
||||
(_M, Res) ->
|
||||
Res
|
||||
end, {error, not_allowed}, auth_modules(Server)).
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
case is_user_exists(User,Server) of
|
||||
true ->
|
||||
{atomic, exists};
|
||||
false ->
|
||||
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
|
||||
true ->
|
||||
lists:foldl(
|
||||
fun(_M, {atomic, ok} = Res) ->
|
||||
Res;
|
||||
(M, _) ->
|
||||
M:try_register(User, Server, Password)
|
||||
end, {error, not_allowed}, auth_modules(Server));
|
||||
false ->
|
||||
{error, not_allowed}
|
||||
end
|
||||
end.
|
||||
|
||||
%% Registered users list do not include anonymous users logged
|
||||
dirty_get_registered_users() ->
|
||||
lists:flatmap(
|
||||
fun(M) ->
|
||||
M:dirty_get_registered_users()
|
||||
end, auth_modules(?MYNAME)).
|
||||
|
||||
%% Registered users list do not include anonymous users logged
|
||||
get_vh_registered_users(Server) ->
|
||||
lists:flatmap(
|
||||
fun(M) ->
|
||||
M:get_vh_registered_users(Server)
|
||||
end, auth_modules(Server)).
|
||||
|
||||
get_password(User, Server) ->
|
||||
lists:foldl(
|
||||
fun(M, false) ->
|
||||
M:get_password(User, Server);
|
||||
(_M, Password) ->
|
||||
Password
|
||||
end, false, auth_modules(Server)).
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
case get_password(User, Server) of
|
||||
false ->
|
||||
"";
|
||||
Password ->
|
||||
Password
|
||||
end.
|
||||
|
||||
%% Returns true if the user exists in the DB or if an anonymous user is logged
|
||||
%% under the given name
|
||||
is_user_exists(User, Server) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:is_user_exists(User, Server)
|
||||
end, auth_modules(Server)).
|
||||
|
||||
%% Check if the user exists in all authentications module except the module
|
||||
%% passed as parameter
|
||||
is_user_exists_in_other_modules(Module, User, Server) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:is_user_exists(User, Server)
|
||||
end, auth_modules(Server)--[Module]).
|
||||
|
||||
remove_user(User, Server) ->
|
||||
lists:foreach(
|
||||
fun(M) ->
|
||||
M:remove_user(User, Server)
|
||||
end, auth_modules(Server)).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
lists:foreach(
|
||||
fun(M) ->
|
||||
M:remove_user(User, Server, Password)
|
||||
end, auth_modules(Server)).
|
||||
|
||||
|
||||
ctl_process_get_registered(_Val, Host, ["registered-users"]) ->
|
||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
||||
NewLine = io_lib:format("~n", []),
|
||||
SUsers = lists:sort(Users),
|
||||
FUsers = lists:map(fun({U, _S}) -> [U, NewLine] end, SUsers),
|
||||
io:format("~s", [FUsers]),
|
||||
{stop, ?STATUS_SUCCESS};
|
||||
ctl_process_get_registered(Val, _Host, _Args) ->
|
||||
Val.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
auth_modules(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
Method = ejabberd_config:get_local_option({auth_method, LServer}),
|
||||
Methods = if
|
||||
Method == undefined -> [];
|
||||
is_list(Method) -> Method;
|
||||
is_atom(Method) -> [Method]
|
||||
end,
|
||||
[list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods].
|
|
@ -1,221 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_anonymous.erl
|
||||
%%% Author : Mickael Remond <mickael.remond@process-one.net>
|
||||
%%% Purpose : Anonymous feature support in ejabberd
|
||||
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%% Anonymous support is based on the work of Magnus Henoch
|
||||
%%% <henoch@dtek.chalmers.se> and heavily extended by Process-one.
|
||||
%%%
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_anonymous).
|
||||
-author('mickael.remond@process-one.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1,
|
||||
allow_anonymous/1,
|
||||
is_sasl_anonymous_enabled/1,
|
||||
is_login_anonymous_enabled/1,
|
||||
anonymous_user_exist/2,
|
||||
allow_multiple_connections/1,
|
||||
register_connection/2,
|
||||
unregister_connection/2
|
||||
]).
|
||||
|
||||
|
||||
%% Function used by ejabberd_auth:
|
||||
-export([login/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password/3,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-record(anonymous, {us, sid}).
|
||||
|
||||
%% Create the anonymous table if at least one virtual host has anonymous features enabled
|
||||
%% Register to login / logout events
|
||||
start(Host) ->
|
||||
%% TODO: Check cluster mode
|
||||
mnesia:create_table(anonymous, [{ram_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, anonymous)}]),
|
||||
%% The hooks are needed to add / remove users from the anonymous tables
|
||||
ejabberd_hooks:add(sm_register_connection_hook, Host,
|
||||
?MODULE, register_connection, 100),
|
||||
ejabberd_hooks:add(sm_remove_connection_hook, Host,
|
||||
?MODULE, unregister_connection, 100),
|
||||
ok.
|
||||
|
||||
%% Return true if anonymous is allowed for host or false otherwise
|
||||
allow_anonymous(Host) ->
|
||||
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
|
||||
|
||||
%% Return true if anonymous mode is enabled and if anonymous protocol is SASL
|
||||
%% anonymous protocol can be: sasl_anon|login_anon|both
|
||||
is_sasl_anonymous_enabled(Host) ->
|
||||
case allow_anonymous(Host) of
|
||||
false -> false;
|
||||
true ->
|
||||
case anonymous_protocol(Host) of
|
||||
sasl_anon -> true;
|
||||
both -> true;
|
||||
_Other -> false
|
||||
end
|
||||
end.
|
||||
|
||||
%% Return true if anonymous login is enabled on the server
|
||||
%% anonymous login can be use using standard authentication method (i.e. with
|
||||
%% clients that do not support anonymous login)
|
||||
is_login_anonymous_enabled(Host) ->
|
||||
case allow_anonymous(Host) of
|
||||
false -> false;
|
||||
true ->
|
||||
case anonymous_protocol(Host) of
|
||||
login_anon -> true;
|
||||
both -> true;
|
||||
_Other -> false
|
||||
end
|
||||
end.
|
||||
|
||||
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
|
||||
%% defaults to login_anon
|
||||
anonymous_protocol(Host) ->
|
||||
case ejabberd_config:get_local_option({anonymous_protocol, Host}) of
|
||||
sasl_anon -> sasl_anon;
|
||||
login_anon -> login_anon;
|
||||
both -> both;
|
||||
_Other -> sasl_anon
|
||||
end.
|
||||
|
||||
%% Return true if multiple connections have been allowed in the config file
|
||||
%% defaults to false
|
||||
allow_multiple_connections(Host) ->
|
||||
case ejabberd_config:get_local_option({allow_multiple_connections, Host}) of
|
||||
true -> true;
|
||||
_Other -> false
|
||||
end.
|
||||
|
||||
%% Check if user exist in the anonymus database
|
||||
anonymous_user_exist(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({anonymous, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_H|_T] ->
|
||||
true
|
||||
end.
|
||||
|
||||
%% Remove connection from Mnesia tables
|
||||
remove_connection(SID, LUser, LServer) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete_object({anonymous, US, SID})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%% Register connection
|
||||
register_connection(SID, #jid{luser = LUser, lserver = LServer}) ->
|
||||
US = {LUser, LServer},
|
||||
mnesia:sync_dirty(
|
||||
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
|
||||
end).
|
||||
|
||||
%% Remove an anonymous user from the anonymous users table
|
||||
unregister_connection(SID, #jid{luser = LUser, lserver = LServer}) ->
|
||||
remove_connection(SID, LUser, LServer).
|
||||
|
||||
%% ---------------------------------
|
||||
%% Specific anonymous auth functions
|
||||
%% ---------------------------------
|
||||
|
||||
%% When anonymous login is enabled, check the password for permenant users
|
||||
%% before allowing access
|
||||
check_password(User, Server, Password) ->
|
||||
check_password(User, Server, Password, undefined, undefined).
|
||||
check_password(User, Server, _Password, _StreamID, _Digest) ->
|
||||
%% We refuse login for registered accounts (They cannot logged but
|
||||
%% they however are "reserved")
|
||||
case ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
|
||||
User, Server) of
|
||||
true -> false;
|
||||
false -> login(User, Server)
|
||||
end.
|
||||
|
||||
login(User, Server) ->
|
||||
case is_login_anonymous_enabled(Server) of
|
||||
false -> false;
|
||||
true ->
|
||||
case anonymous_user_exist(User, Server) of
|
||||
%% Reject the login if an anonymous user with the same login
|
||||
%% is already logged and if multiple login has not been enable
|
||||
%% in the config file.
|
||||
true -> allow_multiple_connections(Server);
|
||||
%% Accept login and add user to the anonymous table
|
||||
false -> true
|
||||
end
|
||||
end.
|
||||
|
||||
%% When anonymous login is enabled, check that the user is permanent before
|
||||
%% changing its password
|
||||
set_password(User, Server, _Password) ->
|
||||
case anonymous_user_exist(User, Server) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
%% When anonymous login is enabled, check if permanent users are allowed on
|
||||
%% the server:
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
|
||||
|
||||
%% Return password of permanent user or false for anonymous users
|
||||
get_password(User, Server) ->
|
||||
get_password(User, Server, "").
|
||||
|
||||
get_password(User, Server, DefaultValue) ->
|
||||
case anonymous_user_exist(User, Server) of
|
||||
%% We return the default value if the user is anonymous
|
||||
true ->
|
||||
DefaultValue;
|
||||
%% We return the permanent user password otherwise
|
||||
false ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% Returns true if the user exists in the DB or if an anonymous user is logged
|
||||
%% under the given name
|
||||
is_user_exists(User, Server) ->
|
||||
anonymous_user_exist(User, Server).
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_external.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via LDAP external script
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_external).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
extauth:start(
|
||||
Host, ejabberd_config:get_local_option({extauth_program, Host})),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password).
|
||||
|
||||
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
extauth:set_password(User, Server, Password).
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
extauth:is_user_exists(User, Server).
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_internal.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via mnesia
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_internal).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(passwd, {us, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
mnesia:create_table(passwd, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
update_table(),
|
||||
ejabberd_ctl:register_commands(
|
||||
Host,
|
||||
[{"registered-users", "list all registered users"}],
|
||||
ejabberd_auth, ctl_process_get_registered),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun() ->
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, US}) of
|
||||
[] ->
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password}),
|
||||
ok;
|
||||
[_E] ->
|
||||
exists
|
||||
end
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
mnesia:dirty_all_keys(passwd).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
mnesia:dirty_select(
|
||||
passwd,
|
||||
[{#passwd{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]).
|
||||
|
||||
get_password(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({passwd, US})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
mnesia:delete({passwd, US}),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
_ ->
|
||||
not_exists
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, passwd),
|
||||
case mnesia:table_info(passwd, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, password] ->
|
||||
?INFO_MSG("Converting passwd table from "
|
||||
"{user, password} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
ejabberd_auth_internal_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, passwd},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
mnesia:transform_table(passwd, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(ejabberd_auth_internal_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#passwd{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
ejabberd_auth_internal_tmp_table,
|
||||
R#passwd{us = {U, Host}})
|
||||
end, ok, passwd)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(passwd),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(passwd),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, ejabberd_auth_internal_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(ejabberd_auth_internal_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating passwd table", []),
|
||||
mnesia:transform_table(passwd, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_ldap.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via LDAP
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_ldap).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_info/2,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
terminate/2,
|
||||
code_change/3
|
||||
]).
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
stop/1,
|
||||
start_link/1,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("eldap/eldap.hrl").
|
||||
|
||||
-record(state, {host,
|
||||
eldap_id,
|
||||
bind_eldap_id,
|
||||
servers,
|
||||
port,
|
||||
dn,
|
||||
password,
|
||||
base,
|
||||
uidattr,
|
||||
uidattr_format,
|
||||
ufilter,
|
||||
sfilter,
|
||||
dn_filter,
|
||||
dn_filter_attrs
|
||||
}).
|
||||
|
||||
%% Unused callbacks.
|
||||
handle_cast(_Request, State) ->
|
||||
{noreply, State}.
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
%% -----
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
start(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
ChildSpec = {
|
||||
Proc, {?MODULE, start_link, [Host]},
|
||||
permanent, 1000, worker, [?MODULE]
|
||||
},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
gen_server:call(Proc, stop),
|
||||
supervisor:terminate_child(ejabberd_sup, Proc),
|
||||
supervisor:delete_child(ejabberd_sup, Proc).
|
||||
|
||||
start_link(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
gen_server:start_link({local, Proc}, ?MODULE, Host, []).
|
||||
|
||||
terminate(_Reason, State) ->
|
||||
ejabberd_ctl:unregister_commands(
|
||||
State#state.host,
|
||||
[{"registered-users", "list all registered users"}],
|
||||
ejabberd_auth, ctl_process_get_registered).
|
||||
|
||||
init(Host) ->
|
||||
State = parse_options(Host),
|
||||
eldap:start_link(State#state.eldap_id,
|
||||
State#state.servers,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password),
|
||||
eldap:start_link(State#state.bind_eldap_id,
|
||||
State#state.servers,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password),
|
||||
ejabberd_ctl:register_commands(
|
||||
Host,
|
||||
[{"registered-users", "list all registered users"}],
|
||||
ejabberd_auth, ctl_process_get_registered),
|
||||
{ok, State}.
|
||||
|
||||
-define(REPLY_TIMEOUT, 10000).
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
Proc = gen_mod:get_module_proc(Server, ?MODULE),
|
||||
case catch gen_server:call(Proc,
|
||||
{check_pass, User, Password}, ?REPLY_TIMEOUT) of
|
||||
{'EXIT', _} ->
|
||||
false;
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
get_vh_registered_users(?MYNAME).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
Proc = gen_mod:get_module_proc(Server, ?MODULE),
|
||||
case catch gen_server:call(Proc,
|
||||
get_vh_registered_users, ?REPLY_TIMEOUT) of
|
||||
{'EXIT', _} ->
|
||||
[];
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
Proc = gen_mod:get_module_proc(Server, ?MODULE),
|
||||
case catch gen_server:call(Proc,
|
||||
{is_user_exists, User}, ?REPLY_TIMEOUT) of
|
||||
{'EXIT', _} ->
|
||||
false;
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
handle_call({check_pass, User, Password}, _From, State) ->
|
||||
Reply = case find_user_dn(User, State) of
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap:bind(State#state.bind_eldap_id, DN, Password) of
|
||||
ok -> true;
|
||||
_ -> false
|
||||
end
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
|
||||
handle_call(get_vh_registered_users, _From, State) ->
|
||||
UA = State#state.uidattr,
|
||||
UAF = State#state.uidattr_format,
|
||||
Eldap_ID = State#state.eldap_id,
|
||||
Server = State#state.host,
|
||||
SortedDNAttrs = usort_attrs(State#state.dn_filter_attrs),
|
||||
Reply = case eldap_filter:parse(State#state.sfilter) of
|
||||
{ok, EldapFilter} ->
|
||||
case eldap:search(Eldap_ID, [{base, State#state.base},
|
||||
{filter, EldapFilter},
|
||||
{attributes, SortedDNAttrs}]) of
|
||||
#eldap_search_result{entries = Entries} ->
|
||||
lists:flatmap(
|
||||
fun(#eldap_entry{attributes = Attrs,
|
||||
object_name = DN}) ->
|
||||
case is_valid_dn(DN, Attrs, State) of
|
||||
false -> [];
|
||||
_ ->
|
||||
case get_ldap_attr(UA, Attrs) of
|
||||
"" -> [];
|
||||
User ->
|
||||
case get_user_part(User, UAF) of
|
||||
{ok, U} ->
|
||||
case jlib:nodeprep(U) of
|
||||
error -> [];
|
||||
LU -> [{LU, jlib:nameprep(Server)}]
|
||||
end;
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
end
|
||||
end, Entries);
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
|
||||
handle_call({is_user_exists, User}, _From, State) ->
|
||||
Reply = case find_user_dn(User, State) of
|
||||
false -> false;
|
||||
_DN -> true
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State};
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, bad_request, State}.
|
||||
|
||||
find_user_dn(User, State) ->
|
||||
DNAttrs = usort_attrs(State#state.dn_filter_attrs),
|
||||
case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
|
||||
{ok, Filter} ->
|
||||
case eldap:search(State#state.eldap_id, [{base, State#state.base},
|
||||
{filter, Filter},
|
||||
{attributes, DNAttrs}]) of
|
||||
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
|
||||
object_name = DN} | _]} ->
|
||||
is_valid_dn(DN, Attrs, State);
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
is_valid_dn(DN, _, #state{dn_filter = undefined}) ->
|
||||
DN;
|
||||
|
||||
is_valid_dn(DN, Attrs, State) ->
|
||||
DNAttrs = State#state.dn_filter_attrs,
|
||||
UA = State#state.uidattr,
|
||||
UAF = State#state.uidattr_format,
|
||||
Values = [{"%s", get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
|
||||
SubstValues = case get_ldap_attr(UA, Attrs) of
|
||||
"" -> Values;
|
||||
S ->
|
||||
case get_user_part(S, UAF) of
|
||||
{ok, U} -> [{"%u", U} | Values];
|
||||
_ -> Values
|
||||
end
|
||||
end ++ [{"%d", State#state.host}, {"%D", DN}],
|
||||
case eldap_filter:parse(State#state.dn_filter, SubstValues) of
|
||||
{ok, EldapFilter} ->
|
||||
case eldap:search(State#state.eldap_id, [
|
||||
{base, State#state.base},
|
||||
{filter, EldapFilter},
|
||||
{attributes, ["dn"]}]) of
|
||||
#eldap_search_result{entries = [_|_]} ->
|
||||
DN;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Auxiliary functions
|
||||
%%%----------------------------------------------------------------------
|
||||
get_user_part(String, Pattern) ->
|
||||
F = fun(S, P) ->
|
||||
First = string:str(P, "%u"),
|
||||
TailLength = length(P) - (First+1),
|
||||
string:sub_string(S, First, length(S) - TailLength)
|
||||
end,
|
||||
case catch F(String, Pattern) of
|
||||
{'EXIT', _} ->
|
||||
{error, badmatch};
|
||||
Result ->
|
||||
case regexp:sub(Pattern, "%u", Result) of
|
||||
{ok, String, _} -> {ok, Result};
|
||||
_ -> {error, badmatch}
|
||||
end
|
||||
end.
|
||||
|
||||
case_insensitive_match(X, Y) ->
|
||||
X1 = stringprep:tolower(X),
|
||||
Y1 = stringprep:tolower(Y),
|
||||
if
|
||||
X1 == Y1 -> true;
|
||||
true -> false
|
||||
end.
|
||||
|
||||
get_ldap_attr(LDAPAttr, Attributes) ->
|
||||
Res = lists:filter(
|
||||
fun({Name, _}) ->
|
||||
case_insensitive_match(Name, LDAPAttr)
|
||||
end, Attributes),
|
||||
case Res of
|
||||
[{_, [Value|_]}] -> Value;
|
||||
_ -> ""
|
||||
end.
|
||||
|
||||
usort_attrs(Attrs) when is_list(Attrs) ->
|
||||
lists:usort(Attrs);
|
||||
|
||||
usort_attrs(_) ->
|
||||
[].
|
||||
|
||||
parse_options(Host) ->
|
||||
Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?MODULE)),
|
||||
Bind_Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
||||
LDAPServers = ejabberd_config:get_local_option({ldap_servers, Host}),
|
||||
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
|
||||
undefined -> 389;
|
||||
P -> P
|
||||
end,
|
||||
RootDN = case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
|
||||
undefined -> "";
|
||||
RDN -> RDN
|
||||
end,
|
||||
Password = case ejabberd_config:get_local_option({ldap_password, Host}) of
|
||||
undefined -> "";
|
||||
Pass -> Pass
|
||||
end,
|
||||
UIDAttr = case ejabberd_config:get_local_option({ldap_uidattr, Host}) of
|
||||
undefined -> "uid";
|
||||
UA -> UA
|
||||
end,
|
||||
UIDAttrFormat = case ejabberd_config:get_local_option({ldap_uidattr_format, Host}) of
|
||||
undefined -> "%u";
|
||||
UAF -> UAF
|
||||
end,
|
||||
SubFilter = "(" ++ UIDAttr ++ "=" ++ UIDAttrFormat ++ ")",
|
||||
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
|
||||
undefined -> SubFilter;
|
||||
"" -> SubFilter;
|
||||
F -> "(&" ++ SubFilter ++ F ++ ")"
|
||||
end,
|
||||
SearchFilter = eldap_filter:do_sub(UserFilter, [{"%u", "*"}]),
|
||||
LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}),
|
||||
{DNFilter, DNFilterAttrs} =
|
||||
case ejabberd_config:get_local_option({ldap_dn_filter, Host}) of
|
||||
undefined -> {undefined, undefined};
|
||||
{DNF, DNFA} -> {DNF, DNFA}
|
||||
end,
|
||||
#state{host = Host,
|
||||
eldap_id = Eldap_ID,
|
||||
bind_eldap_id = Bind_Eldap_ID,
|
||||
servers = LDAPServers,
|
||||
port = LDAPPort,
|
||||
dn = RootDN,
|
||||
password = Password,
|
||||
base = LDAPBase,
|
||||
uidattr = UIDAttr,
|
||||
uidattr_format = UIDAttrFormat,
|
||||
ufilter = UserFilter,
|
||||
sfilter = SearchFilter,
|
||||
dn_filter = DNFilter,
|
||||
dn_filter_attrs = DNFilterAttrs
|
||||
}.
|
|
@ -1,215 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via ODBC
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_odbc).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(passwd, {user, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
ChildSpec =
|
||||
{gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
|
||||
{ejabberd_odbc_sup, start_link, [Host]},
|
||||
temporary,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_odbc_sup]},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec),
|
||||
ejabberd_ctl:register_commands(
|
||||
Host,
|
||||
[{"registered-users", "list all registered users"}],
|
||||
ejabberd_auth, ctl_process_get_registered),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{Passwd}]} ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, invalid_jid};
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
LServer = jlib:nameprep(Server),
|
||||
catch odbc_queries:set_password_t(LServer, Username, Pass)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, invalid_jid};
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:add_user(LServer, Username, Pass) of
|
||||
{updated, 1} ->
|
||||
{atomic, ok};
|
||||
_ ->
|
||||
{atomic, exists}
|
||||
end
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
get_vh_registered_users(?MYNAME).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:list_users(LServer) of
|
||||
{selected, ["username"], Res} ->
|
||||
[{U, LServer} || {U} <- Res];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_password(User, Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
"";
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
Password;
|
||||
_ ->
|
||||
""
|
||||
end
|
||||
end.
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{_Password}]} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
catch odbc_queries:del_user(LServer, Username),
|
||||
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
|
||||
[User, Server])
|
||||
end.
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
LServer = jlib:nameprep(Server),
|
||||
F = fun() ->
|
||||
Result = odbc_queries:del_user_return_password(
|
||||
LServer, Username, Pass),
|
||||
case Result of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
|
||||
[User, Server]),
|
||||
ok;
|
||||
{selected, ["password"], []} ->
|
||||
not_exists;
|
||||
_ ->
|
||||
not_allowed
|
||||
end
|
||||
end,
|
||||
{atomic, Result} = odbc_queries:transaction(LServer, F),
|
||||
Result
|
||||
end.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,228 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_config.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Load config file
|
||||
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_config).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0, load_file/1,
|
||||
add_global_option/2, add_local_option/2,
|
||||
get_global_option/1, get_local_option/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(config, {key, value}).
|
||||
-record(local_config, {key, value}).
|
||||
-record(state, {opts = [],
|
||||
hosts = [],
|
||||
override_local = false,
|
||||
override_global = false,
|
||||
override_acls = false}).
|
||||
|
||||
start() ->
|
||||
mnesia:create_table(config,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, config)}]),
|
||||
mnesia:add_table_copy(config, node(), ram_copies),
|
||||
mnesia:create_table(local_config,
|
||||
[{disc_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, local_config)}]),
|
||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||
Config = case application:get_env(config) of
|
||||
{ok, Path} -> Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_CONFIG_PATH") of
|
||||
false ->
|
||||
?CONFIG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
end,
|
||||
load_file(Config).
|
||||
|
||||
|
||||
load_file(File) ->
|
||||
case file:consult(File) of
|
||||
{ok, Terms} ->
|
||||
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
||||
Res = lists:foldl(fun process_term/2, State, Terms),
|
||||
set_opts(Res);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't load config file ~p: ~p", [File, Reason]),
|
||||
exit(file:format_error(Reason))
|
||||
end.
|
||||
|
||||
search_hosts(Term, State) ->
|
||||
case Term of
|
||||
{host, Host} ->
|
||||
if
|
||||
State#state.hosts == [] ->
|
||||
add_option(hosts, [Host], State#state{hosts = [Host]});
|
||||
true ->
|
||||
?ERROR_MSG("Can't load config file: "
|
||||
"too many hosts definitions", []),
|
||||
exit("too many hosts definitions")
|
||||
end;
|
||||
{hosts, Hosts} ->
|
||||
if
|
||||
State#state.hosts == [] ->
|
||||
add_option(hosts, Hosts, State#state{hosts = Hosts});
|
||||
true ->
|
||||
?ERROR_MSG("Can't load config file: "
|
||||
"too many hosts definitions", []),
|
||||
exit("too many hosts definitions")
|
||||
end;
|
||||
_ ->
|
||||
State
|
||||
end.
|
||||
|
||||
process_term(Term, State) ->
|
||||
case Term of
|
||||
override_global ->
|
||||
State#state{override_global = true};
|
||||
override_local ->
|
||||
State#state{override_local = true};
|
||||
override_acls ->
|
||||
State#state{override_acls = true};
|
||||
{acl, ACLName, ACLData} ->
|
||||
process_host_term(Term, global, State);
|
||||
{access, RuleName, Rules} ->
|
||||
process_host_term(Term, global, State);
|
||||
{shaper, Name, Data} ->
|
||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||
State, State#state.hosts);
|
||||
{host, Host} ->
|
||||
State;
|
||||
{hosts, Hosts} ->
|
||||
State;
|
||||
{host_config, Host, Terms} ->
|
||||
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
|
||||
State, Terms);
|
||||
{listen, Val} ->
|
||||
add_option(listen, Val, State);
|
||||
{outgoing_s2s_port, Port} ->
|
||||
add_option(outgoing_s2s_port, Port, State);
|
||||
{s2s_use_starttls, Port} ->
|
||||
add_option(s2s_use_starttls, Port, State);
|
||||
{s2s_certfile, CertFile} ->
|
||||
add_option(s2s_certfile, CertFile, State);
|
||||
{domain_certfile, Domain, CertFile} ->
|
||||
add_option({domain_certfile, Domain}, CertFile, State);
|
||||
{Opt, Val} ->
|
||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||
State, State#state.hosts)
|
||||
end.
|
||||
|
||||
process_host_term(Term, Host, State) ->
|
||||
case Term of
|
||||
{acl, ACLName, ACLData} ->
|
||||
State#state{opts =
|
||||
[acl:to_record(Host, ACLName, ACLData) | State#state.opts]};
|
||||
{access, RuleName, Rules} ->
|
||||
State#state{opts = [#config{key = {access, RuleName, Host},
|
||||
value = Rules} |
|
||||
State#state.opts]};
|
||||
{shaper, Name, Data} ->
|
||||
State#state{opts = [#config{key = {shaper, Name, Host},
|
||||
value = Data} |
|
||||
State#state.opts]};
|
||||
{host, Host} ->
|
||||
State;
|
||||
{hosts, Hosts} ->
|
||||
State;
|
||||
{Opt, Val} ->
|
||||
add_option({Opt, Host}, Val, State)
|
||||
end.
|
||||
|
||||
add_option(Opt, Val, State) ->
|
||||
Table = case Opt of
|
||||
hosts ->
|
||||
config;
|
||||
language ->
|
||||
config;
|
||||
_ ->
|
||||
local_config
|
||||
end,
|
||||
case Table of
|
||||
config ->
|
||||
State#state{opts = [#config{key = Opt, value = Val} |
|
||||
State#state.opts]};
|
||||
local_config ->
|
||||
State#state{opts = [#local_config{key = Opt, value = Val} |
|
||||
State#state.opts]}
|
||||
end.
|
||||
|
||||
|
||||
set_opts(State) ->
|
||||
Opts = lists:reverse(State#state.opts),
|
||||
F = fun() ->
|
||||
if
|
||||
State#state.override_global ->
|
||||
Ksg = mnesia:all_keys(config),
|
||||
lists:foreach(fun(K) ->
|
||||
mnesia:delete({config, K})
|
||||
end, Ksg);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if
|
||||
State#state.override_local ->
|
||||
Ksl = mnesia:all_keys(local_config),
|
||||
lists:foreach(fun(K) ->
|
||||
mnesia:delete({local_config, K})
|
||||
end, Ksl);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if
|
||||
State#state.override_acls ->
|
||||
Ksa = mnesia:all_keys(acl),
|
||||
lists:foreach(fun(K) ->
|
||||
mnesia:delete({acl, K})
|
||||
end, Ksa);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(fun(R) ->
|
||||
mnesia:write(R)
|
||||
end, Opts)
|
||||
end,
|
||||
{atomic, _} = mnesia:transaction(F).
|
||||
|
||||
|
||||
add_global_option(Opt, Val) ->
|
||||
mnesia:transaction(fun() ->
|
||||
mnesia:write(#config{key = Opt,
|
||||
value = Val})
|
||||
end).
|
||||
|
||||
add_local_option(Opt, Val) ->
|
||||
mnesia:transaction(fun() ->
|
||||
mnesia:write(#local_config{key = Opt,
|
||||
value = Val})
|
||||
end).
|
||||
|
||||
|
||||
get_global_option(Opt) ->
|
||||
case ets:lookup(config, Opt) of
|
||||
[#config{value = Val}] ->
|
||||
Val;
|
||||
_ ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
get_local_option(Opt) ->
|
||||
case ets:lookup(local_config, Opt) of
|
||||
[#local_config{value = Val}] ->
|
||||
Val;
|
||||
_ ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
|
|
@ -1,351 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_ctl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Ejabberd admin tool
|
||||
%%% Created : 11 Jan 2004 by Alexey Shchepin <alex@alex.sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_ctl).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
init/0,
|
||||
process/1,
|
||||
register_commands/3,
|
||||
register_commands/4,
|
||||
unregister_commands/3,
|
||||
unregister_commands/4]).
|
||||
|
||||
-include("ejabberd_ctl.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
start() ->
|
||||
case init:get_plain_arguments() of
|
||||
[SNode | Args] ->
|
||||
Node = list_to_atom(SNode),
|
||||
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
|
||||
{badrpc, Reason} ->
|
||||
io:format("RPC failed on the node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
S ->
|
||||
S
|
||||
end,
|
||||
halt(Status);
|
||||
_ ->
|
||||
print_usage(),
|
||||
halt(?STATUS_USAGE)
|
||||
end.
|
||||
|
||||
init() ->
|
||||
ets:new(ejabberd_ctl_cmds, [named_table, set, public]),
|
||||
ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]).
|
||||
|
||||
|
||||
process(["status"]) ->
|
||||
{InternalStatus, ProvidedStatus} = init:get_status(),
|
||||
io:format("Node ~p is ~p. Status: ~p~n",
|
||||
[node(), InternalStatus, ProvidedStatus]),
|
||||
case lists:keysearch(ejabberd, 1, application:which_applications()) of
|
||||
false ->
|
||||
io:format("ejabberd is not running~n", []),
|
||||
?STATUS_ERROR;
|
||||
{value,_Version} ->
|
||||
io:format("ejabberd is running~n", []),
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
process(["stop"]) ->
|
||||
init:stop(),
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
process(["restart"]) ->
|
||||
init:restart(),
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
process(["reopen-log"]) ->
|
||||
ejabberd_logger_h:reopen_log(),
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
process(["register", User, Server, Password]) ->
|
||||
case ejabberd_auth:try_register(User, Server, Password) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{atomic, exists} ->
|
||||
io:format("User ~p already registered at node ~p~n",
|
||||
[User ++ "@" ++ Server, node()]),
|
||||
?STATUS_ERROR;
|
||||
{error, Reason} ->
|
||||
io:format("Can't register user ~p at node ~p: ~p~n",
|
||||
[User ++ "@" ++ Server, node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["unregister", User, Server]) ->
|
||||
case ejabberd_auth:remove_user(User, Server) of
|
||||
{error, Reason} ->
|
||||
io:format("Can't unregister user ~p at node ~p: ~p~n",
|
||||
[User ++ "@" ++ Server, node(), Reason]),
|
||||
?STATUS_ERROR;
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
process(["backup", Path]) ->
|
||||
case mnesia:backup(Path) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't store backup in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["dump", Path]) ->
|
||||
case dump_to_textfile(Path) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't store dump in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["load", Path]) ->
|
||||
case mnesia:load_textfile(Path) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't load dump in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["restore", Path]) ->
|
||||
case ejabberd_admin:restore(Path) of
|
||||
{atomic, _} ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't restore backup from ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR;
|
||||
{aborted,{no_exists,Table}} ->
|
||||
io:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.~n",
|
||||
[filename:absname(Path), node(), Table]),
|
||||
?STATUS_ERROR;
|
||||
{aborted,enoent} ->
|
||||
io:format("Can't restore backup from ~p at node ~p: File not found.~n",
|
||||
[filename:absname(Path), node()]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["install-fallback", Path]) ->
|
||||
case mnesia:install_fallback(Path) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't install fallback from ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["import-file", Path]) ->
|
||||
case jd2ejd:import_file(Path) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["import-dir", Path]) ->
|
||||
case jd2ejd:import_dir(Path) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), node(), Reason]),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["delete-expired-messages"]) ->
|
||||
mod_offline:remove_expired_messages(),
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
process(["delete-old-messages", Days]) ->
|
||||
case catch list_to_integer(Days) of
|
||||
{'EXIT',{Reason, _Stack}} ->
|
||||
io:format("Can't delete old messages (~p). Please pass an integer as parameter.~n",
|
||||
[Reason]),
|
||||
?STATUS_ERROR;
|
||||
Integer when Integer >= 0 ->
|
||||
{atomic, _} = mod_offline:remove_old_messages(Integer),
|
||||
io:format("Removed messages older than ~s days~n", [Days]),
|
||||
?STATUS_SUCCESS;
|
||||
Integer ->
|
||||
io:format("Can't delete old messages. Please pass a positive integer as parameter.~n", []),
|
||||
?STATUS_ERROR
|
||||
end;
|
||||
|
||||
process(["vhost", H | Args]) ->
|
||||
case jlib:nameprep(H) of
|
||||
false ->
|
||||
io:format("Bad hostname: ~p~n", [H]),
|
||||
?STATUS_ERROR;
|
||||
Host ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
ejabberd_ctl_process, Host, false, [Host, Args]) of
|
||||
false ->
|
||||
print_vhost_usage(Host),
|
||||
?STATUS_USAGE;
|
||||
Status ->
|
||||
Status
|
||||
end
|
||||
end;
|
||||
|
||||
process(Args) ->
|
||||
case ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of
|
||||
false ->
|
||||
print_usage(),
|
||||
?STATUS_USAGE;
|
||||
Status ->
|
||||
Status
|
||||
end.
|
||||
|
||||
|
||||
print_usage() ->
|
||||
CmdDescs =
|
||||
[{"status", "get ejabberd status"},
|
||||
{"stop", "stop ejabberd"},
|
||||
{"restart", "restart ejabberd"},
|
||||
{"reopen-log", "reopen log file"},
|
||||
{"register user server password", "register a user"},
|
||||
{"unregister user server", "unregister a user"},
|
||||
{"backup file", "store a database backup to file"},
|
||||
{"restore file", "restore a database backup from file"},
|
||||
{"install-fallback file", "install a database fallback from file"},
|
||||
{"dump file", "dump a database to a text file"},
|
||||
{"load file", "restore a database from a text file"},
|
||||
{"import-file file", "import user data from jabberd 1.4 spool file"},
|
||||
{"import-dir dir", "import user data from jabberd 1.4 spool directory"},
|
||||
{"delete-expired-messages", "delete expired offline messages from database"},
|
||||
{"delete-old-messages n", "delete offline messages older than n days from database"},
|
||||
{"vhost host ...", "execute host-specific commands"}] ++
|
||||
ets:tab2list(ejabberd_ctl_cmds),
|
||||
MaxCmdLen =
|
||||
lists:max(lists:map(
|
||||
fun({Cmd, _Desc}) ->
|
||||
length(Cmd)
|
||||
end, CmdDescs)),
|
||||
NewLine = io_lib:format("~n", []),
|
||||
FmtCmdDescs =
|
||||
lists:map(
|
||||
fun({Cmd, Desc}) ->
|
||||
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
|
||||
Desc, NewLine]
|
||||
end, CmdDescs),
|
||||
io:format(
|
||||
"Usage: ejabberdctl node command~n"
|
||||
"~n"
|
||||
"Available commands:~n"
|
||||
++ FmtCmdDescs ++
|
||||
"~n"
|
||||
"Example:~n"
|
||||
" ejabberdctl ejabberd@host restart~n"
|
||||
).
|
||||
|
||||
print_vhost_usage(Host) ->
|
||||
CmdDescs =
|
||||
ets:select(ejabberd_ctl_host_cmds,
|
||||
[{{Host, '$1', '$2'}, [], [{{'$1', '$2'}}]}]),
|
||||
MaxCmdLen =
|
||||
if
|
||||
CmdDescs == [] ->
|
||||
0;
|
||||
true ->
|
||||
lists:max(lists:map(
|
||||
fun({Cmd, _Desc}) ->
|
||||
length(Cmd)
|
||||
end, CmdDescs))
|
||||
end,
|
||||
NewLine = io_lib:format("~n", []),
|
||||
FmtCmdDescs =
|
||||
lists:map(
|
||||
fun({Cmd, Desc}) ->
|
||||
[" ", Cmd, string:chars($\s, MaxCmdLen - length(Cmd) + 2),
|
||||
Desc, NewLine]
|
||||
end, CmdDescs),
|
||||
io:format(
|
||||
"Usage: ejabberdctl node vhost host command~n"
|
||||
"~n"
|
||||
"Available commands:~n"
|
||||
++ FmtCmdDescs ++
|
||||
"~n"
|
||||
).
|
||||
|
||||
register_commands(CmdDescs, Module, Function) ->
|
||||
ets:insert(ejabberd_ctl_cmds, CmdDescs),
|
||||
ejabberd_hooks:add(ejabberd_ctl_process,
|
||||
Module, Function, 50),
|
||||
ok.
|
||||
|
||||
register_commands(Host, CmdDescs, Module, Function) ->
|
||||
ets:insert(ejabberd_ctl_host_cmds,
|
||||
[{Host, Cmd, Desc} || {Cmd, Desc} <- CmdDescs]),
|
||||
ejabberd_hooks:add(ejabberd_ctl_process, Host,
|
||||
Module, Function, 50),
|
||||
ok.
|
||||
|
||||
unregister_commands(CmdDescs, Module, Function) ->
|
||||
lists:foreach(fun(CmdDesc) ->
|
||||
ets:delete_object(ejabberd_ctl_cmds, CmdDesc)
|
||||
end, CmdDescs),
|
||||
ejabberd_hooks:delete(ejabberd_ctl_process,
|
||||
Module, Function, 50),
|
||||
ok.
|
||||
|
||||
unregister_commands(Host, CmdDescs, Module, Function) ->
|
||||
lists:foreach(fun({Cmd, Desc}) ->
|
||||
ets:delete_object(ejabberd_ctl_host_cmds,
|
||||
{Host, Cmd, Desc})
|
||||
end, CmdDescs),
|
||||
ejabberd_hooks:delete(ejabberd_ctl_process,
|
||||
Module, Function, 50),
|
||||
ok.
|
||||
|
||||
dump_to_textfile(File) ->
|
||||
dump_to_textfile(mnesia:system_info(is_running), file:open(File, write)).
|
||||
dump_to_textfile(yes, {ok, F}) ->
|
||||
Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)),
|
||||
Tabs = lists:filter(
|
||||
fun(T) ->
|
||||
case mnesia:table_info(T, storage_type) of
|
||||
disc_copies -> true;
|
||||
disc_only_copies -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, Tabs1),
|
||||
Defs = lists:map(
|
||||
fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)},
|
||||
{attributes, mnesia:table_info(T, attributes)}]}
|
||||
end,
|
||||
Tabs),
|
||||
io:format(F, "~p.~n", [{tables, Defs}]),
|
||||
lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
|
||||
file:close(F);
|
||||
dump_to_textfile(_, {ok, F}) ->
|
||||
file:close(F),
|
||||
{error, mnesia_not_running};
|
||||
dump_to_textfile(_, {error, Reason}) ->
|
||||
{error, Reason}.
|
||||
|
||||
|
||||
dump_tab(F, T) ->
|
||||
W = mnesia:table_info(T, wild_pattern),
|
||||
{atomic,All} = mnesia:transaction(
|
||||
fun() -> mnesia:match_object(T, W, read) end),
|
||||
lists:foreach(
|
||||
fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
|
|
@ -1,12 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_ctl.hrl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 13 Feb 2006 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
-define(STATUS_BADRPC, 3).
|
|
@ -1,199 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_hooks.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Manage hooks
|
||||
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_hooks).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% External exports
|
||||
-export([start_link/0,
|
||||
add/4,
|
||||
delete/4,
|
||||
run/2,
|
||||
run_fold/3,
|
||||
add/5,
|
||||
delete/5,
|
||||
run/3,
|
||||
run_fold/4]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
code_change/3,
|
||||
handle_info/2,
|
||||
terminate/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []).
|
||||
|
||||
add(Hook, Module, Function, Seq) ->
|
||||
add(Hook, global, Module, Function, Seq).
|
||||
|
||||
add(Hook, Host, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {add, Hook, Host, Module, Function, Seq}).
|
||||
|
||||
delete(Hook, Module, Function, Seq) ->
|
||||
delete(Hook, global, Module, Function, Seq).
|
||||
|
||||
delete(Hook, Host, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {delete, Hook, Host, Module, Function, Seq}).
|
||||
|
||||
run(Hook, Args) ->
|
||||
run(Hook, global, Args).
|
||||
|
||||
run(Hook, Host, Args) ->
|
||||
case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
run1(Ls, Hook, Args);
|
||||
[] ->
|
||||
ok
|
||||
end.
|
||||
|
||||
run_fold(Hook, Val, Args) ->
|
||||
run_fold(Hook, global, Val, Args).
|
||||
|
||||
run_fold(Hook, Host, Val, Args) ->
|
||||
case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
run_fold1(Ls, Hook, Val, Args);
|
||||
[] ->
|
||||
Val
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_server
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
ets:new(hooks, [named_table]),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_call/3
|
||||
%% Returns: {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} | (terminate/2 is called)
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_call({add, Hook, Host, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
El = {Seq, Module, Function},
|
||||
case lists:member(El, Ls) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
NewLs = lists:merge(Ls, [El]),
|
||||
ets:insert(hooks, {{Hook, Host}, NewLs}),
|
||||
ok
|
||||
end;
|
||||
[] ->
|
||||
NewLs = [{Seq, Module, Function}],
|
||||
ets:insert(hooks, {{Hook, Host}, NewLs}),
|
||||
ok
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
handle_call({delete, Hook, Host, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
NewLs = lists:delete({Seq, Module, Function}, Ls),
|
||||
ets:insert(hooks, {{Hook, Host}, NewLs}),
|
||||
ok;
|
||||
[] ->
|
||||
ok
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
handle_call(Request, From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_cast/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_cast(Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/2
|
||||
%% Purpose: Shutdown the server
|
||||
%% Returns: any (ignored by gen_server)
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, State) ->
|
||||
ok.
|
||||
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
run1([], Hook, Args) ->
|
||||
ok;
|
||||
run1([{_Seq, Module, Function} | Ls], Hook, Args) ->
|
||||
case catch apply(Module, Function, Args) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nrunning hook: ~p",
|
||||
[Reason, {Hook, Args}]),
|
||||
run1(Ls, Hook, Args);
|
||||
stop ->
|
||||
ok;
|
||||
_ ->
|
||||
run1(Ls, Hook, Args)
|
||||
end.
|
||||
|
||||
|
||||
run_fold1([], Hook, Val, Args) ->
|
||||
Val;
|
||||
run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) ->
|
||||
case catch apply(Module, Function, [Val | Args]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nrunning hook: ~p",
|
||||
[Reason, {Hook, Args}]),
|
||||
run_fold1(Ls, Hook, Val, Args);
|
||||
stop ->
|
||||
stopped;
|
||||
{stop, NewVal} ->
|
||||
NewVal;
|
||||
NewVal ->
|
||||
run_fold1(Ls, Hook, NewVal, Args)
|
||||
end.
|
||||
|
||||
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_listener.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_listener).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start_link/0, init/1, start/3,
|
||||
init/3,
|
||||
init_ssl/4,
|
||||
start_listener/3,
|
||||
stop_listener/1,
|
||||
add_listener/3,
|
||||
delete_listener/1
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []).
|
||||
|
||||
|
||||
init(_) ->
|
||||
case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
ignore;
|
||||
Ls ->
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
lists:map(
|
||||
fun({Port, Module, Opts}) ->
|
||||
{Port,
|
||||
{?MODULE, start, [Port, Module, Opts]},
|
||||
transient,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[?MODULE]}
|
||||
end, Ls)}}
|
||||
end.
|
||||
|
||||
|
||||
start(Port, Module, Opts) ->
|
||||
case lists:keysearch(ssl, 1, Opts) of
|
||||
{value, {ssl, SSLOpts}} ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init_ssl,
|
||||
[Port, Module, Opts, SSLOpts])};
|
||||
_ ->
|
||||
case lists:member(ssl, Opts) of
|
||||
true ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init_ssl,
|
||||
[Port, Module, Opts, []])};
|
||||
false ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, init,
|
||||
[Port, Module, Opts])}
|
||||
end
|
||||
end.
|
||||
|
||||
init(Port, Module, Opts) ->
|
||||
SockOpts = lists:filter(fun({ip, _}) -> true;
|
||||
(inet6) -> true;
|
||||
(inet) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
|
||||
Res = gen_tcp:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true},
|
||||
{keepalive, true} |
|
||||
SockOpts]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to open socket for ~p: ~p",
|
||||
[{Port, Module, Opts}, Reason]),
|
||||
error
|
||||
end.
|
||||
|
||||
accept(ListenSocket, Module, Opts) ->
|
||||
case gen_tcp:accept(ListenSocket) of
|
||||
{ok, Socket} ->
|
||||
case {inet:sockname(Socket), inet:peername(Socket)} of
|
||||
{{ok, Addr}, {ok, PAddr}} ->
|
||||
?INFO_MSG("(~w) Accepted connection ~w -> ~w",
|
||||
[Socket, PAddr, Addr]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{ok, Pid} = Module:start({gen_tcp, Socket}, Opts),
|
||||
case gen_tcp:controlling_process(Socket, Pid) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, _Reason} ->
|
||||
gen_tcp:close(Socket)
|
||||
end,
|
||||
Module:become_controller(Pid),
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("(~w) Failed TCP accept: ~w",
|
||||
[ListenSocket, Reason]),
|
||||
accept(ListenSocket, Module, Opts)
|
||||
end.
|
||||
|
||||
|
||||
init_ssl(Port, Module, Opts, SSLOpts) ->
|
||||
SockOpts = lists:filter(fun({ip, _}) -> true;
|
||||
(inet6) -> true;
|
||||
(inet) -> true;
|
||||
({verify, _}) -> true;
|
||||
({depth, _}) -> true;
|
||||
({certfile, _}) -> true;
|
||||
({keyfile, _}) -> true;
|
||||
({password, _}) -> true;
|
||||
({cacertfile, _}) -> true;
|
||||
({ciphers, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
Res = ssl:listen(Port, [binary,
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{nodelay, true} |
|
||||
SockOpts ++ SSLOpts]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to open socket for ~p: ~p",
|
||||
[{Port, Module, Opts}, Reason]),
|
||||
error
|
||||
end.
|
||||
|
||||
accept_ssl(ListenSocket, Module, Opts) ->
|
||||
case ssl:accept(ListenSocket, 200) of
|
||||
{ok, Socket} ->
|
||||
case {ssl:sockname(Socket), ssl:peername(Socket)} of
|
||||
{{ok, Addr}, {ok, PAddr}} ->
|
||||
?INFO_MSG("(~w) Accepted SSL connection ~w -> ~w",
|
||||
[Socket, PAddr, Addr]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{ok, Pid} = Module:start({ssl, Socket}, Opts),
|
||||
catch ssl:controlling_process(Socket, Pid),
|
||||
Module:become_controller(Pid),
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
{error, timeout} ->
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("(~w) Failed SSL handshake: ~w",
|
||||
[ListenSocket, Reason]),
|
||||
accept_ssl(ListenSocket, Module, Opts)
|
||||
end.
|
||||
|
||||
|
||||
start_listener(Port, Module, Opts) ->
|
||||
ChildSpec = {Port,
|
||||
{?MODULE, start, [Port, Module, Opts]},
|
||||
transient,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
supervisor:start_child(ejabberd_listeners, ChildSpec).
|
||||
|
||||
stop_listener(Port) ->
|
||||
supervisor:terminate_child(ejabberd_listeners, Port),
|
||||
supervisor:delete_child(ejabberd_listeners, Port).
|
||||
|
||||
add_listener(Port, Module, Opts) ->
|
||||
Ports = case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
[];
|
||||
Ls ->
|
||||
Ls
|
||||
end,
|
||||
Ports1 = lists:keydelete(Port, 1, Ports),
|
||||
Ports2 = [{Port, Module, Opts} | Ports1],
|
||||
ejabberd_config:add_local_option(listen, Ports2),
|
||||
start_listener(Port, Module, Opts).
|
||||
|
||||
delete_listener(Port) ->
|
||||
Ports = case ejabberd_config:get_local_option(listen) of
|
||||
undefined ->
|
||||
[];
|
||||
Ls ->
|
||||
Ls
|
||||
end,
|
||||
Ports1 = lists:keydelete(Port, 1, Ports),
|
||||
ejabberd_config:add_local_option(listen, Ports1),
|
||||
stop_listener(Port).
|
||||
|
|
@ -1,245 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_local.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Route local packets
|
||||
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_local).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([route/3,
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
refresh_iq_handlers/0,
|
||||
bounce_resource_packet/3
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-define(IQTABLE, local_iqtable).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
process_iq(From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{xmlns = XMLNS} ->
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
ResIQ /= ignore ->
|
||||
ejabberd_router:route(
|
||||
To, From, jlib:iq_to_xml(ResIQ));
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
reply ->
|
||||
ok;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ok
|
||||
end.
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
|
||||
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
ejabberd_local ! {unregister_iq_handler, Host, XMLNS}.
|
||||
|
||||
refresh_iq_handlers() ->
|
||||
ejabberd_local ! refresh_iq_handlers.
|
||||
|
||||
bounce_resource_packet(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, {apply, ?MODULE, route}),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||
?MODULE, bounce_resource_packet, 100)
|
||||
end, ?MYHOSTS),
|
||||
catch ets:new(?IQTABLE, [named_table, public]),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({route, From, To, Packet}, State) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
|
||||
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
|
||||
catch mod_disco:register_feature(Host, XMLNS),
|
||||
{noreply, State};
|
||||
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
|
||||
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
|
||||
catch mod_disco:register_feature(Host, XMLNS),
|
||||
{noreply, State};
|
||||
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
|
||||
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(?IQTABLE, {XMLNS, Host}),
|
||||
catch mod_disco:unregister_feature(Host, XMLNS),
|
||||
{noreply, State};
|
||||
handle_info(refresh_iq_handlers, State) ->
|
||||
lists:foreach(
|
||||
fun(T) ->
|
||||
case T of
|
||||
{{XMLNS, Host}, _Module, _Function, _Opts} ->
|
||||
catch mod_disco:register_feature(Host, XMLNS);
|
||||
{{XMLNS, Host}, _Module, _Function} ->
|
||||
catch mod_disco:register_feature(Host, XMLNS);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, ets:tab2list(?IQTABLE)),
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
if
|
||||
To#jid.luser /= "" ->
|
||||
ejabberd_sm:route(From, To, Packet);
|
||||
To#jid.lresource == "" ->
|
||||
{xmlelement, Name, _Attrs, _Els} = Packet,
|
||||
case Name of
|
||||
"iq" ->
|
||||
process_iq(From, To, Packet);
|
||||
"message" ->
|
||||
ok;
|
||||
"presence" ->
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
true ->
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"error" -> ok;
|
||||
"result" -> ok;
|
||||
_ ->
|
||||
ejabberd_hooks:run(local_send_to_resource_hook,
|
||||
To#jid.lserver,
|
||||
[From, To, Packet])
|
||||
end
|
||||
end.
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_logger_h.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 23 Oct 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_logger_h).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
%%-compile(export_all).
|
||||
%%-export([Function/Arity, ...]).
|
||||
|
||||
-behaviour(gen_event).
|
||||
|
||||
%% gen_event callbacks
|
||||
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
|
||||
code_change/3, reopen_log/0]).
|
||||
|
||||
-record(state, {fd, file}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_event
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, State} |
|
||||
%% Other
|
||||
%%----------------------------------------------------------------------
|
||||
init(File) ->
|
||||
case file:open(File, [append, raw]) of
|
||||
{ok, Fd} ->
|
||||
{ok, #state{fd = Fd, file = File}};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/2
|
||||
%% Returns: {ok, State} |
|
||||
%% {swap_handler, Args1, State1, Mod2, Args2} |
|
||||
%% remove_handler
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(Event, State) ->
|
||||
write_event(State#state.fd, {erlang:localtime(), Event}),
|
||||
{ok, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_call/2
|
||||
%% Returns: {ok, Reply, State} |
|
||||
%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |
|
||||
%% {remove_handler, Reply}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_call(_Request, State) ->
|
||||
Reply = ok,
|
||||
{ok, Reply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/2
|
||||
%% Returns: {ok, State} |
|
||||
%% {swap_handler, Args1, State1, Mod2, Args2} |
|
||||
%% remove_handler
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({'EXIT', _Fd, _Reason}, _State) ->
|
||||
remove_handler;
|
||||
handle_info({emulator, _GL, reopen}, State) ->
|
||||
file:close(State#state.fd),
|
||||
rotate_log(State#state.file),
|
||||
case file:open(State#state.file, [append, raw]) of
|
||||
{ok, Fd} ->
|
||||
{ok, State#state{fd = Fd}};
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
handle_info({emulator, GL, Chars}, State) ->
|
||||
write_event(State#state.fd, {erlang:localtime(), {emulator, GL, Chars}}),
|
||||
{ok, State};
|
||||
handle_info(_Info, State) ->
|
||||
{ok, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/2
|
||||
%% Purpose: Shutdown the server
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
reopen_log() ->
|
||||
error_logger ! {emulator, noproc, reopen}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
% Copied from erlang_logger_file_h.erl
|
||||
write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
|
||||
T = write_time(Time),
|
||||
case catch io_lib:format(add_node(Format,Pid), Args) of
|
||||
S when list(S) ->
|
||||
file:write(Fd, io_lib:format(T ++ S, []));
|
||||
_ ->
|
||||
F = add_node("ERROR: ~p - ~p~n", Pid),
|
||||
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
|
||||
end;
|
||||
write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
|
||||
T = write_time(Time),
|
||||
case catch io_lib:format(Chars, []) of
|
||||
S when list(S) ->
|
||||
file:write(Fd, io_lib:format(T ++ S, []));
|
||||
_ ->
|
||||
file:write(Fd, io_lib:format(T ++ "ERROR: ~p ~n", [Chars]))
|
||||
end;
|
||||
write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
|
||||
T = write_time(Time),
|
||||
file:write(Fd, io_lib:format(T ++ add_node("~p~n",Pid), [Info]));
|
||||
write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
|
||||
T = write_time(Time),
|
||||
S = format_report(Rep),
|
||||
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
|
||||
write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
|
||||
T = write_time(Time, "INFO REPORT"),
|
||||
S = format_report(Rep),
|
||||
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), []));
|
||||
write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
|
||||
T = write_time(Time, "INFO REPORT"),
|
||||
case catch io_lib:format(add_node(Format,Pid), Args) of
|
||||
S when list(S) ->
|
||||
file:write(Fd, io_lib:format(T ++ S, []));
|
||||
_ ->
|
||||
F = add_node("ERROR: ~p - ~p~n", Pid),
|
||||
file:write(Fd, io_lib:format(T ++ F, [Format,Args]))
|
||||
end;
|
||||
write_event(_, _) ->
|
||||
ok.
|
||||
|
||||
format_report(Rep) when list(Rep) ->
|
||||
case string_p(Rep) of
|
||||
true ->
|
||||
io_lib:format("~s~n",[Rep]);
|
||||
_ ->
|
||||
format_rep(Rep)
|
||||
end;
|
||||
format_report(Rep) ->
|
||||
io_lib:format("~p~n",[Rep]).
|
||||
|
||||
format_rep([{Tag,Data}|Rep]) ->
|
||||
io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
|
||||
format_rep([Other|Rep]) ->
|
||||
io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
|
||||
format_rep(_) ->
|
||||
[].
|
||||
|
||||
add_node(X, Pid) when atom(X) ->
|
||||
add_node(atom_to_list(X), Pid);
|
||||
add_node(X, Pid) when node(Pid) /= node() ->
|
||||
lists:concat([X,"** at node ",node(Pid)," **~n"]);
|
||||
add_node(X, _) ->
|
||||
X.
|
||||
|
||||
string_p([]) ->
|
||||
false;
|
||||
string_p(Term) ->
|
||||
string_p1(Term).
|
||||
|
||||
string_p1([H|T]) when integer(H), H >= $\s, H < 255 ->
|
||||
string_p1(T);
|
||||
string_p1([$\n|T]) -> string_p1(T);
|
||||
string_p1([$\r|T]) -> string_p1(T);
|
||||
string_p1([$\t|T]) -> string_p1(T);
|
||||
string_p1([$\v|T]) -> string_p1(T);
|
||||
string_p1([$\b|T]) -> string_p1(T);
|
||||
string_p1([$\f|T]) -> string_p1(T);
|
||||
string_p1([$\e|T]) -> string_p1(T);
|
||||
string_p1([H|T]) when list(H) ->
|
||||
case string_p1(H) of
|
||||
true -> string_p1(T);
|
||||
_ -> false
|
||||
end;
|
||||
string_p1([]) -> true;
|
||||
string_p1(_) -> false.
|
||||
|
||||
write_time(Time) -> write_time(Time, "ERROR REPORT").
|
||||
|
||||
write_time({{Y,Mo,D},{H,Mi,S}}, Type) ->
|
||||
io_lib:format("~n=~s==== ~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w ===~n",
|
||||
[Type, Y, Mo, D, H, Mi, S]).
|
||||
|
||||
%% Rename the log file if it the filename exists
|
||||
%% This is needed in systems when the file must be closed before rotation (Windows).
|
||||
%% On most Unix-like system, the file can be renamed from the command line and
|
||||
%%the log can directly be reopened.
|
||||
rotate_log(Filename) ->
|
||||
case file:read_file_info(Filename) of
|
||||
{ok, _FileInfo} ->
|
||||
RotationName = filename:rootname(Filename),
|
||||
file:rename(Filename, [RotationName, "-old.log"]),
|
||||
ok;
|
||||
{error, _Reason} ->
|
||||
ok
|
||||
end.
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_receiver.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Socket receiver for C2S and S2S connections
|
||||
%%% Created : 10 Nov 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_receiver).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/5,
|
||||
start/3,
|
||||
start/4,
|
||||
change_shaper/2,
|
||||
reset_stream/1,
|
||||
starttls/2,
|
||||
compress/2,
|
||||
become_controller/1,
|
||||
close/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(state, {socket,
|
||||
sock_mod,
|
||||
shaper_state,
|
||||
c2s_pid,
|
||||
max_stanza_size,
|
||||
xml_stream_state,
|
||||
timeout}).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link(Socket, SockMod, Shaper, MaxStanzaSize, C2SPid) ->
|
||||
gen_server:start_link(
|
||||
?MODULE, [Socket, SockMod, Shaper, MaxStanzaSize, C2SPid], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start(Socket, SockMod, Shaper) ->
|
||||
start(Socket, SockMod, Shaper, infinity).
|
||||
|
||||
start(Socket, SockMod, Shaper, MaxStanzaSize) ->
|
||||
{ok, Pid} = supervisor:start_child(
|
||||
ejabberd_receiver_sup,
|
||||
[Socket, SockMod, Shaper, MaxStanzaSize, self()]),
|
||||
Pid.
|
||||
|
||||
change_shaper(Pid, Shaper) ->
|
||||
gen_server:cast(Pid, {change_shaper, Shaper}).
|
||||
|
||||
reset_stream(Pid) ->
|
||||
gen_server:call(Pid, reset_stream).
|
||||
|
||||
starttls(Pid, TLSSocket) ->
|
||||
gen_server:call(Pid, {starttls, TLSSocket}).
|
||||
|
||||
compress(Pid, ZlibSocket) ->
|
||||
gen_server:call(Pid, {compress, ZlibSocket}).
|
||||
|
||||
become_controller(Pid) ->
|
||||
gen_server:call(Pid, become_controller).
|
||||
|
||||
close(Pid) ->
|
||||
gen_server:cast(Pid, close).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([Socket, SockMod, Shaper, MaxStanzaSize, C2SPid]) ->
|
||||
XMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
|
||||
ShaperState = shaper:new(Shaper),
|
||||
Timeout = case SockMod of
|
||||
ssl ->
|
||||
20;
|
||||
_ ->
|
||||
infinity
|
||||
end,
|
||||
{ok, #state{socket = Socket,
|
||||
sock_mod = SockMod,
|
||||
shaper_state = ShaperState,
|
||||
c2s_pid = C2SPid,
|
||||
max_stanza_size = MaxStanzaSize,
|
||||
xml_stream_state = XMLStreamState,
|
||||
timeout = Timeout}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call({starttls, TLSSocket}, _From,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid,
|
||||
max_stanza_size = MaxStanzaSize} = State) ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
|
||||
NewState = State#state{socket = TLSSocket,
|
||||
sock_mod = tls,
|
||||
xml_stream_state = NewXMLStreamState},
|
||||
case tls:recv_data(TLSSocket, "") of
|
||||
{ok, TLSData} ->
|
||||
{reply, ok, process_data(TLSData, NewState)};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, ok, NewState}
|
||||
end;
|
||||
handle_call({compress, ZlibSocket}, _From,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid,
|
||||
max_stanza_size = MaxStanzaSize} = State) ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
|
||||
NewState = State#state{socket = ZlibSocket,
|
||||
sock_mod = ejabberd_zlib,
|
||||
xml_stream_state = NewXMLStreamState},
|
||||
case ejabberd_zlib:recv_data(ZlibSocket, "") of
|
||||
{ok, ZlibData} ->
|
||||
{reply, ok, process_data(ZlibData, NewState)};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, ok, NewState}
|
||||
end;
|
||||
handle_call(reset_stream, _From,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid,
|
||||
max_stanza_size = MaxStanzaSize} = State) ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
|
||||
Reply = ok,
|
||||
{reply, Reply, State#state{xml_stream_state = NewXMLStreamState}};
|
||||
handle_call(become_controller, _From, State) ->
|
||||
activate_socket(State),
|
||||
Reply = ok,
|
||||
{reply, Reply, State};
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast({change_shaper, Shaper}, State) ->
|
||||
NewShaperState = shaper:new(Shaper),
|
||||
{noreply, State#state{shaper_state = NewShaperState}};
|
||||
handle_cast(close, State) ->
|
||||
{stop, normal, State};
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({Tag, _TCPSocket, Data},
|
||||
#state{socket = Socket,
|
||||
sock_mod = SockMod} = State)
|
||||
when (Tag == tcp) or (Tag == ssl) ->
|
||||
case SockMod of
|
||||
tls ->
|
||||
case tls:recv_data(Socket, Data) of
|
||||
{ok, TLSData} ->
|
||||
{noreply, process_data(TLSData, State)};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, State}
|
||||
end;
|
||||
ejabberd_zlib ->
|
||||
case ejabberd_zlib:recv_data(Socket, Data) of
|
||||
{ok, ZlibData} ->
|
||||
{noreply, process_data(ZlibData, State)};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, State}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, process_data(Data, State)}
|
||||
end;
|
||||
handle_info({Tag, _TCPSocket}, State)
|
||||
when (Tag == tcp_closed) or (Tag == ssl_closed) ->
|
||||
{stop, normal, State};
|
||||
handle_info({Tag, _TCPSocket, Reason}, State)
|
||||
when (Tag == tcp_error) or (Tag == ssl_error) ->
|
||||
case Reason of
|
||||
timeout ->
|
||||
{noreply, State};
|
||||
_ ->
|
||||
{stop, normal, State}
|
||||
end;
|
||||
handle_info({timeout, _Ref, activate}, State) ->
|
||||
activate_socket(State),
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, #state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid} = State) ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
gen_fsm:send_event(C2SPid, closed),
|
||||
catch (State#state.sock_mod):close(State#state.socket),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
activate_socket(#state{socket = Socket,
|
||||
sock_mod = SockMod}) ->
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket, [{active, once}]);
|
||||
_ ->
|
||||
SockMod:setopts(Socket, [{active, once}])
|
||||
end.
|
||||
|
||||
process_data(Data,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
shaper_state = ShaperState} = State) ->
|
||||
?DEBUG("Received XML on stream = ~p", [binary_to_list(Data)]),
|
||||
XMLStreamState1 = xml_stream:parse(XMLStreamState, Data),
|
||||
{NewShaperState, Pause} = shaper:update(ShaperState, size(Data)),
|
||||
if
|
||||
Pause > 0 ->
|
||||
erlang:start_timer(Pause, self(), activate);
|
||||
true ->
|
||||
activate_socket(State)
|
||||
end,
|
||||
State#state{xml_stream_state = XMLStreamState1,
|
||||
shaper_state = NewShaperState}.
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_router.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Main router
|
||||
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_router).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([route/3,
|
||||
register_route/1,
|
||||
register_route/2,
|
||||
register_routes/1,
|
||||
unregister_route/1,
|
||||
unregister_routes/1,
|
||||
dirty_get_all_routes/0,
|
||||
dirty_get_all_domains/0
|
||||
]).
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(route, {domain, pid, local_hint}).
|
||||
-record(state, {}).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_route(Domain) ->
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = LDomain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
register_route(Domain, LocalHint) ->
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = LDomain,
|
||||
pid = Pid,
|
||||
local_hint = LocalHint})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
register_routes(Domains) ->
|
||||
lists:foreach(fun(Domain) ->
|
||||
register_route(Domain)
|
||||
end, Domains).
|
||||
|
||||
unregister_route(Domain) ->
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:delete_object(#route{domain = LDomain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
unregister_routes(Domains) ->
|
||||
lists:foreach(fun(Domain) ->
|
||||
unregister_route(Domain)
|
||||
end, Domains).
|
||||
|
||||
|
||||
dirty_get_all_routes() ->
|
||||
lists:usort(mnesia:dirty_all_keys(route)) -- ?MYHOSTS.
|
||||
|
||||
dirty_get_all_domains() ->
|
||||
lists:usort(mnesia:dirty_all_keys(route)).
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
update_tables(),
|
||||
mnesia:create_table(route,
|
||||
[{ram_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes,
|
||||
record_info(fields, route)}]),
|
||||
mnesia:add_table_copy(route, node(), ram_copies),
|
||||
mnesia:subscribe({table, route, simple}),
|
||||
lists:foreach(
|
||||
fun(Pid) ->
|
||||
erlang:monitor(process, Pid)
|
||||
end,
|
||||
mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({route, From, To, Packet}, State) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}},
|
||||
State) ->
|
||||
erlang:monitor(process, Pid),
|
||||
{noreply, State};
|
||||
handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
|
||||
F = fun() ->
|
||||
Es = mnesia:select(
|
||||
route,
|
||||
[{#route{pid = Pid, _ = '_'},
|
||||
[],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
do_route(OrigFrom, OrigTo, OrigPacket) ->
|
||||
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n",
|
||||
[OrigFrom, OrigTo, OrigPacket]),
|
||||
LOrigDstDomain = OrigTo#jid.lserver,
|
||||
case ejabberd_hooks:run_fold(filter_packet,
|
||||
{OrigFrom, OrigTo, OrigPacket}, []) of
|
||||
{From, To, Packet} ->
|
||||
LDstDomain = To#jid.lserver,
|
||||
case mnesia:dirty_read(route, LDstDomain) of
|
||||
[] ->
|
||||
ejabberd_s2s:route(From, To, Packet);
|
||||
[R] ->
|
||||
Pid = R#route.pid,
|
||||
if
|
||||
node(Pid) == node() ->
|
||||
case R#route.local_hint of
|
||||
{apply, Module, Function} ->
|
||||
Module:Function(From, To, Packet);
|
||||
_ ->
|
||||
Pid ! {route, From, To, Packet}
|
||||
end;
|
||||
true ->
|
||||
Pid ! {route, From, To, Packet}
|
||||
end;
|
||||
Rs ->
|
||||
case [R || R <- Rs, node(R#route.pid) == node()] of
|
||||
[] ->
|
||||
R = lists:nth(erlang:phash(now(), length(Rs)), Rs),
|
||||
Pid = R#route.pid,
|
||||
Pid ! {route, From, To, Packet};
|
||||
LRs ->
|
||||
LRs,
|
||||
R = lists:nth(erlang:phash(now(), length(LRs)), LRs),
|
||||
Pid = R#route.pid,
|
||||
case R#route.local_hint of
|
||||
{apply, Module, Function} ->
|
||||
Module:Function(From, To, Packet);
|
||||
_ ->
|
||||
Pid ! {route, From, To, Packet}
|
||||
end
|
||||
end
|
||||
end;
|
||||
drop ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
|
||||
update_tables() ->
|
||||
case catch mnesia:table_info(route, attributes) of
|
||||
[domain, node, pid] ->
|
||||
mnesia:delete_table(route);
|
||||
[domain, pid] ->
|
||||
mnesia:delete_table(route);
|
||||
[domain, pid, local_hint] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(local_route, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_route);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
|
@ -1,277 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_s2s.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : S2S connections manager
|
||||
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_s2s).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0,
|
||||
route/3,
|
||||
have_connection/1,
|
||||
get_key/1,
|
||||
try_register/1,
|
||||
remove_connection/1,
|
||||
dirty_get_connections/0,
|
||||
ctl_process/2
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_ctl.hrl").
|
||||
|
||||
-record(s2s, {fromto, pid, key}).
|
||||
-record(state, {}).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
remove_connection(FromTo) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({s2s, FromTo})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
have_connection(FromTo) ->
|
||||
case catch mnesia:dirty_read(s2s, FromTo) of
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_key(FromTo) ->
|
||||
case catch mnesia:dirty_read(s2s, FromTo) of
|
||||
[E] ->
|
||||
E#s2s.key;
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
try_register(FromTo) ->
|
||||
Key = randoms:get_string(),
|
||||
F = fun() ->
|
||||
case mnesia:read({s2s, FromTo}) of
|
||||
[] ->
|
||||
mnesia:write(#s2s{fromto = FromTo,
|
||||
pid = self(),
|
||||
key = Key}),
|
||||
{key, Key};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
dirty_get_connections() ->
|
||||
mnesia:dirty_all_keys(s2s).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
update_tables(),
|
||||
mnesia:create_table(s2s, [{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, s2s)}]),
|
||||
mnesia:add_table_copy(s2s, node(), ram_copies),
|
||||
mnesia:subscribe(system),
|
||||
ejabberd_ctl:register_commands(
|
||||
[{"incoming-s2s-number", "print number of incoming s2s connections on the node"},
|
||||
{"outgoing-s2s-number", "print number of outgoing s2s connections on the node"}],
|
||||
?MODULE, ctl_process),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
clean_table_from_bad_node(Node),
|
||||
{noreply, State};
|
||||
handle_info({route, From, To, Packet}, State) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
clean_table_from_bad_node(Node) ->
|
||||
F = fun() ->
|
||||
Es = mnesia:select(
|
||||
s2s,
|
||||
[{#s2s{pid = '$1', _ = '_'},
|
||||
[{'==', {node, '$1'}, Node}],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
case find_connection(From, To) of
|
||||
{atomic, Pid} when pid(Pid) ->
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
% TODO
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
send_element(Pid, {xmlelement, Name, NewAttrs, Els}),
|
||||
ok;
|
||||
{aborted, Reason} ->
|
||||
?DEBUG("delivery failed: ~p~n", [Reason]),
|
||||
false
|
||||
end.
|
||||
|
||||
find_connection(From, To) ->
|
||||
#jid{lserver = MyServer} = From,
|
||||
#jid{lserver = Server} = To,
|
||||
FromTo = {MyServer, Server},
|
||||
case catch mnesia:dirty_read(s2s, FromTo) of
|
||||
{'EXIT', Reason} ->
|
||||
{aborted, Reason};
|
||||
[] ->
|
||||
?DEBUG("starting new s2s connection~n", []),
|
||||
Key = randoms:get_string(),
|
||||
{ok, Pid} = ejabberd_s2s_out:start(MyServer, Server, {new, Key}),
|
||||
F = fun() ->
|
||||
case mnesia:read({s2s, FromTo}) of
|
||||
[El] ->
|
||||
El#s2s.pid;
|
||||
[] ->
|
||||
mnesia:write(#s2s{fromto = FromTo,
|
||||
pid = Pid,
|
||||
key = Key}),
|
||||
Pid
|
||||
end
|
||||
end,
|
||||
TRes = mnesia:transaction(F),
|
||||
ejabberd_s2s_out:start_connection(Pid),
|
||||
TRes;
|
||||
[El] ->
|
||||
{atomic, El#s2s.pid}
|
||||
end.
|
||||
|
||||
send_element(Pid, El) ->
|
||||
Pid ! {send_element, El}.
|
||||
|
||||
ctl_process(_Val, ["incoming-s2s-number"]) ->
|
||||
N = length(supervisor:which_children(ejabberd_s2s_in_sup)),
|
||||
io:format("~p~n", [N]),
|
||||
{stop, ?STATUS_SUCCESS};
|
||||
ctl_process(_Val, ["outgoing-s2s-number"]) ->
|
||||
N = length(supervisor:which_children(ejabberd_s2s_out_sup)),
|
||||
io:format("~p~n", [N]),
|
||||
{stop, ?STATUS_SUCCESS};
|
||||
ctl_process(Val, _Args) ->
|
||||
Val.
|
||||
|
||||
update_tables() ->
|
||||
case catch mnesia:table_info(s2s, attributes) of
|
||||
[fromto, node, key] ->
|
||||
mnesia:transform_table(s2s, ignore, [fromto, pid, key]),
|
||||
mnesia:clear_table(s2s);
|
||||
[fromto, pid, key] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(local_s2s, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_s2s);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
|
@ -1,686 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_s2s_in.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Serve incoming s2s connection
|
||||
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_s2s_in).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start/2,
|
||||
start_link/2,
|
||||
become_controller/1,
|
||||
match_domain/2]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_stream/2,
|
||||
wait_for_feature_request/2,
|
||||
stream_established/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include_lib("ssl/include/PKIX1Explicit88.hrl").
|
||||
-include_lib("ssl/include/PKIX1Implicit88.hrl").
|
||||
-include("XmppAddr.hrl").
|
||||
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(state, {socket,
|
||||
sockmod,
|
||||
receiver,
|
||||
streamid,
|
||||
shaper,
|
||||
tls = false,
|
||||
tls_enabled = false,
|
||||
tls_options = [],
|
||||
authenticated = false,
|
||||
auth_domain,
|
||||
connections = ?DICT:new(),
|
||||
timer}).
|
||||
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
-define(STREAM_HEADER(Version),
|
||||
("<?xml version='1.0'?>"
|
||||
"<stream:stream "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"xmlns='jabber:server' "
|
||||
"xmlns:db='jabber:server:dialback' "
|
||||
"id='" ++ StateData#state.streamid ++ "'" ++ Version ++ ">")
|
||||
).
|
||||
|
||||
-define(STREAM_TRAILER, "</stream:stream>").
|
||||
|
||||
-define(INVALID_NAMESPACE_ERR,
|
||||
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
|
||||
|
||||
-define(HOST_UNKNOWN_ERR,
|
||||
xml:element_to_string(?SERR_HOST_UNKNOWN)).
|
||||
|
||||
-define(INVALID_XML_ERR,
|
||||
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(SockData, Opts) ->
|
||||
supervisor:start_child(ejabberd_s2s_in_sup, [SockData, Opts]).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
gen_fsm:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS).
|
||||
|
||||
become_controller(Pid) ->
|
||||
gen_fsm:send_all_state_event(Pid, become_controller).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([{SockMod, Socket}, Opts]) ->
|
||||
?INFO_MSG("started: ~p", [{SockMod, Socket}]),
|
||||
Shaper = case lists:keysearch(shaper, 1, Opts) of
|
||||
{value, {_, S}} -> S;
|
||||
_ -> none
|
||||
end,
|
||||
MaxStanzaSize =
|
||||
case lists:keysearch(max_stanza_size, 1, Opts) of
|
||||
{value, {_, Size}} -> Size;
|
||||
_ -> infinity
|
||||
end,
|
||||
ReceiverPid = ejabberd_receiver:start(
|
||||
Socket, SockMod, none, MaxStanzaSize),
|
||||
StartTLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||
undefined ->
|
||||
false;
|
||||
UseStartTLS ->
|
||||
UseStartTLS
|
||||
end,
|
||||
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
|
||||
undefined ->
|
||||
[];
|
||||
CertFile ->
|
||||
[{certfile, CertFile}]
|
||||
end,
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, wait_for_stream,
|
||||
#state{socket = Socket,
|
||||
sockmod = SockMod,
|
||||
receiver = ReceiverPid,
|
||||
streamid = new_id(),
|
||||
shaper = Shaper,
|
||||
tls = StartTLS,
|
||||
tls_enabled = false,
|
||||
tls_options = TLSOpts,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
case {xml:get_attr_s("xmlns", Attrs),
|
||||
xml:get_attr_s("xmlns:db", Attrs),
|
||||
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||
{"jabber:server", _, true} when
|
||||
StateData#state.tls and (not StateData#state.authenticated) ->
|
||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||
SASL =
|
||||
if
|
||||
StateData#state.tls_enabled ->
|
||||
case tls:get_peer_certificate(StateData#state.socket) of
|
||||
{ok, _Cert} ->
|
||||
case tls:get_verify_result(
|
||||
StateData#state.socket) of
|
||||
0 ->
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, "EXTERNAL"}]}]}];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
error ->
|
||||
[]
|
||||
end;
|
||||
true ->
|
||||
[]
|
||||
end,
|
||||
StartTLS = if
|
||||
StateData#state.tls_enabled ->
|
||||
[];
|
||||
true ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}]
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
SASL ++ StartTLS}),
|
||||
{next_state, wait_for_feature_request, StateData};
|
||||
{"jabber:server", _, true} when
|
||||
StateData#state.authenticated ->
|
||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{next_state, stream_established, StateData};
|
||||
{"jabber:server", "jabber:server:dialback", _} ->
|
||||
send_text(StateData, ?STREAM_HEADER("")),
|
||||
{next_state, stream_established, StateData};
|
||||
_ ->
|
||||
send_text(StateData, ?INVALID_NAMESPACE_ERR),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?STREAM_HEADER("") ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_stream(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_stream(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = El,
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
SockMod = StateData#state.sockmod,
|
||||
case {xml:get_attr_s("xmlns", Attrs), Name} of
|
||||
{?NS_TLS, "starttls"} when TLS == true,
|
||||
TLSEnabled == false,
|
||||
SockMod == gen_tcp ->
|
||||
?INFO_MSG("starttls", []),
|
||||
Socket = StateData#state.socket,
|
||||
TLSOpts = StateData#state.tls_options,
|
||||
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||
ejabberd_receiver:starttls(StateData#state.receiver, TLSSocket),
|
||||
send_element(StateData,
|
||||
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []}),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{sockmod = tls,
|
||||
socket = TLSSocket,
|
||||
streamid = new_id(),
|
||||
tls_enabled = true
|
||||
}};
|
||||
{?NS_SASL, "auth"} when TLSEnabled ->
|
||||
Mech = xml:get_attr_s("mechanism", Attrs),
|
||||
case Mech of
|
||||
"EXTERNAL" ->
|
||||
Auth = jlib:decode_base64(xml:get_cdata(Els)),
|
||||
AuthDomain = jlib:nameprep(Auth),
|
||||
AuthRes =
|
||||
case tls:get_peer_certificate(StateData#state.socket) of
|
||||
{ok, Cert} ->
|
||||
case tls:get_verify_result(
|
||||
StateData#state.socket) of
|
||||
0 ->
|
||||
case AuthDomain of
|
||||
error ->
|
||||
false;
|
||||
_ ->
|
||||
case idna:domain_utf8_to_ascii(AuthDomain) of
|
||||
false ->
|
||||
false;
|
||||
PCAuthDomain ->
|
||||
lists:any(
|
||||
fun(D) ->
|
||||
match_domain(
|
||||
PCAuthDomain, D)
|
||||
end, get_cert_domains(Cert))
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
error ->
|
||||
false
|
||||
end,
|
||||
if
|
||||
AuthRes ->
|
||||
ejabberd_receiver:reset_stream(
|
||||
StateData#state.receiver),
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
?INFO_MSG("(~w) Accepted s2s authentication for ~s",
|
||||
[StateData#state.socket, AuthDomain]),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
authenticated = true,
|
||||
auth_domain = AuthDomain
|
||||
}};
|
||||
true ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, "invalid-mechanism", [], []}]}),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
stream_established({xmlstreamelement, El}, StateData)
|
||||
end;
|
||||
|
||||
wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_feature_request({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_feature_request(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
stream_established({xmlstreamelement, El}, StateData) ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
case is_key_packet(El) of
|
||||
{key, To, From, Id, Key} ->
|
||||
?INFO_MSG("GET KEY: ~p", [{To, From, Id, Key}]),
|
||||
LTo = jlib:nameprep(To),
|
||||
LFrom = jlib:nameprep(From),
|
||||
case lists:member(LTo, ejabberd_router:dirty_get_all_domains()) of
|
||||
true ->
|
||||
ejabberd_s2s_out:start(To, From,
|
||||
{verify, self(),
|
||||
Key, StateData#state.streamid}),
|
||||
Conns = ?DICT:store({LFrom, LTo}, wait_for_verification,
|
||||
StateData#state.connections),
|
||||
change_shaper(StateData, LTo, jlib:make_jid("", LFrom, "")),
|
||||
{next_state,
|
||||
stream_established,
|
||||
StateData#state{connections = Conns,
|
||||
timer = Timer}};
|
||||
_ ->
|
||||
send_text(StateData, ?HOST_UNKNOWN_ERR),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
{verify, To, From, Id, Key} ->
|
||||
?INFO_MSG("VERIFY KEY: ~p", [{To, From, Id, Key}]),
|
||||
LTo = jlib:nameprep(To),
|
||||
LFrom = jlib:nameprep(From),
|
||||
Key1 = ejabberd_s2s:get_key({LTo, LFrom}),
|
||||
Type = if Key == Key1 -> "valid";
|
||||
true -> "invalid"
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement,
|
||||
"db:verify",
|
||||
[{"from", To},
|
||||
{"to", From},
|
||||
{"id", Id},
|
||||
{"type", Type}],
|
||||
[]}),
|
||||
{next_state, stream_established, StateData#state{timer = Timer}};
|
||||
_ ->
|
||||
NewEl = jlib:remove_attr("xmlns", El),
|
||||
{xmlelement, Name, Attrs, _Els} = NewEl,
|
||||
From_s = xml:get_attr_s("from", Attrs),
|
||||
From = jlib:string_to_jid(From_s),
|
||||
To_s = xml:get_attr_s("to", Attrs),
|
||||
To = jlib:string_to_jid(To_s),
|
||||
if
|
||||
(To /= error) and (From /= error) ->
|
||||
LFrom = From#jid.lserver,
|
||||
LTo = To#jid.lserver,
|
||||
if
|
||||
StateData#state.authenticated ->
|
||||
case (LFrom == StateData#state.auth_domain)
|
||||
andalso
|
||||
lists:member(
|
||||
LTo,
|
||||
ejabberd_router:dirty_get_all_domains()) of
|
||||
true ->
|
||||
if ((Name == "iq") or
|
||||
(Name == "message") or
|
||||
(Name == "presence")) ->
|
||||
ejabberd_router:route(
|
||||
From, To, NewEl);
|
||||
true ->
|
||||
error
|
||||
end;
|
||||
false ->
|
||||
error
|
||||
end;
|
||||
true ->
|
||||
case ?DICT:find({LFrom, LTo},
|
||||
StateData#state.connections) of
|
||||
{ok, established} ->
|
||||
if ((Name == "iq") or
|
||||
(Name == "message") or
|
||||
(Name == "presence")) ->
|
||||
ejabberd_router:route(
|
||||
From, To, NewEl);
|
||||
true ->
|
||||
error
|
||||
end;
|
||||
_ ->
|
||||
error
|
||||
end
|
||||
end;
|
||||
true ->
|
||||
error
|
||||
end,
|
||||
{next_state, stream_established, StateData#state{timer = Timer}}
|
||||
end;
|
||||
|
||||
stream_established({valid, From, To}, StateData) ->
|
||||
send_element(StateData,
|
||||
{xmlelement,
|
||||
"db:result",
|
||||
[{"from", To},
|
||||
{"to", From},
|
||||
{"type", "valid"}],
|
||||
[]}),
|
||||
LFrom = jlib:nameprep(From),
|
||||
LTo = jlib:nameprep(To),
|
||||
NSD = StateData#state{
|
||||
connections = ?DICT:store({LFrom, LTo}, established,
|
||||
StateData#state.connections)},
|
||||
{next_state, stream_established, NSD};
|
||||
|
||||
stream_established({invalid, From, To}, StateData) ->
|
||||
send_element(StateData,
|
||||
{xmlelement,
|
||||
"db:result",
|
||||
[{"from", To},
|
||||
{"to", From},
|
||||
{"type", "invalid"}],
|
||||
[]}),
|
||||
LFrom = jlib:nameprep(From),
|
||||
LTo = jlib:nameprep(To),
|
||||
NSD = StateData#state{
|
||||
connections = ?DICT:erase({LFrom, LTo},
|
||||
StateData#state.connections)},
|
||||
{next_state, stream_established, NSD};
|
||||
|
||||
stream_established({xmlstreamend, _Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(become_controller, StateName, StateData) ->
|
||||
ok = (StateData#state.sockmod):controlling_process(
|
||||
StateData#state.socket,
|
||||
StateData#state.receiver),
|
||||
ejabberd_receiver:become_controller(StateData#state.receiver),
|
||||
{next_state, StateName, StateData};
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData, Text),
|
||||
{next_state, StateName, StateData};
|
||||
|
||||
handle_info({timeout, Timer, _}, _StateName,
|
||||
#state{timer = Timer} = StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
?INFO_MSG("terminated: ~p", [Reason]),
|
||||
ejabberd_receiver:close(StateData#state.receiver),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
|
||||
|
||||
change_shaper(StateData, Host, JID) ->
|
||||
Shaper = acl:match_rule(Host, StateData#state.shaper, JID),
|
||||
ejabberd_receiver:change_shaper(StateData#state.receiver, Shaper).
|
||||
|
||||
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
||||
{key,
|
||||
xml:get_attr_s("to", Attrs),
|
||||
xml:get_attr_s("from", Attrs),
|
||||
xml:get_attr_s("id", Attrs),
|
||||
xml:get_cdata(Els)};
|
||||
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
|
||||
{verify,
|
||||
xml:get_attr_s("to", Attrs),
|
||||
xml:get_attr_s("from", Attrs),
|
||||
xml:get_attr_s("id", Attrs),
|
||||
xml:get_cdata(Els)};
|
||||
is_key_packet(_) ->
|
||||
false.
|
||||
|
||||
|
||||
get_cert_domains(Cert) ->
|
||||
{rdnSequence, Subject} =
|
||||
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject,
|
||||
Extensions =
|
||||
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.extensions,
|
||||
lists:flatmap(
|
||||
fun(#'AttributeTypeAndValue'{type = ?'id-at-commonName',
|
||||
value = Val}) ->
|
||||
case 'PKIX1Explicit88':decode('X520CommonName', Val) of
|
||||
{ok, {_, D1}} ->
|
||||
D = if
|
||||
is_list(D1) -> D1;
|
||||
is_binary(D1) -> binary_to_list(D1);
|
||||
true -> error
|
||||
end,
|
||||
if
|
||||
D /= error ->
|
||||
case jlib:string_to_jid(D) of
|
||||
#jid{luser = "",
|
||||
lserver = LD,
|
||||
lresource = ""} ->
|
||||
[LD];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
true ->
|
||||
[]
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
(_) ->
|
||||
[]
|
||||
end, lists:flatten(Subject)) ++
|
||||
lists:flatmap(
|
||||
fun(#'Extension'{extnID = ?'id-ce-subjectAltName',
|
||||
extnValue = Val}) ->
|
||||
BVal = if
|
||||
is_list(Val) -> list_to_binary(Val);
|
||||
is_binary(Val) -> Val;
|
||||
true -> Val
|
||||
end,
|
||||
case 'PKIX1Implicit88':decode('SubjectAltName', BVal) of
|
||||
{ok, SANs} ->
|
||||
lists:flatmap(
|
||||
fun({otherName,
|
||||
#'AnotherName'{'type-id' = ?'id-on-xmppAddr',
|
||||
value = XmppAddr
|
||||
}}) ->
|
||||
case 'XmppAddr':decode(
|
||||
'XmppAddr', XmppAddr) of
|
||||
{ok, D} when is_binary(D) ->
|
||||
case jlib:string_to_jid(
|
||||
binary_to_list(D)) of
|
||||
#jid{luser = "",
|
||||
lserver = LD,
|
||||
lresource = ""} ->
|
||||
case idna:domain_utf8_to_ascii(LD) of
|
||||
false ->
|
||||
[];
|
||||
PCLD ->
|
||||
[PCLD]
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
({dNSName, D}) when is_list(D) ->
|
||||
case jlib:string_to_jid(D) of
|
||||
#jid{luser = "",
|
||||
lserver = LD,
|
||||
lresource = ""} ->
|
||||
[LD];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
(_) ->
|
||||
[]
|
||||
end, SANs);
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
(_) ->
|
||||
[]
|
||||
end, Extensions).
|
||||
|
||||
match_domain(Domain, Domain) ->
|
||||
true;
|
||||
match_domain(Domain, Pattern) ->
|
||||
DLabels = string:tokens(Domain, "."),
|
||||
PLabels = string:tokens(Pattern, "."),
|
||||
match_labels(DLabels, PLabels).
|
||||
|
||||
match_labels([], []) ->
|
||||
true;
|
||||
match_labels([], [_ | _]) ->
|
||||
false;
|
||||
match_labels([_ | _], []) ->
|
||||
false;
|
||||
match_labels([DL | DLabels], [PL | PLabels]) ->
|
||||
case lists:all(fun(C) -> (($a =< C) andalso (C =< $z))
|
||||
orelse (($0 =< C) andalso (C =< $9))
|
||||
orelse (C == $-) orelse (C == $*)
|
||||
end, PL) of
|
||||
true ->
|
||||
Regexp = regexp:sh_to_awk(PL),
|
||||
case regexp:match(DL, Regexp) of
|
||||
{match, _, _} ->
|
||||
match_labels(DLabels, PLabels);
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
false ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
|
@ -1,857 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_s2s_out.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_s2s_out).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start/3,
|
||||
start_link/3,
|
||||
start_connection/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
open_socket/2,
|
||||
wait_for_stream/2,
|
||||
wait_for_validation/2,
|
||||
wait_for_features/2,
|
||||
wait_for_auth_result/2,
|
||||
wait_for_starttls_proceed/2,
|
||||
reopen_socket/2,
|
||||
stream_established/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
code_change/4,
|
||||
test_get_addr_port/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {socket, receiver,
|
||||
sockmod,
|
||||
streamid,
|
||||
use_v10,
|
||||
tls = false,
|
||||
tls_required = false,
|
||||
tls_enabled = false,
|
||||
tls_options = [],
|
||||
authenticated = false,
|
||||
db_enabled = true,
|
||||
try_auth = true,
|
||||
myname, server, queue,
|
||||
new = false, verify = false,
|
||||
timer}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
-define(STREAM_HEADER,
|
||||
"<?xml version='1.0'?>"
|
||||
"<stream:stream "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"xmlns='jabber:server' "
|
||||
"xmlns:db='jabber:server:dialback' "
|
||||
"to='~s'~s>"
|
||||
).
|
||||
|
||||
-define(STREAM_TRAILER, "</stream:stream>").
|
||||
|
||||
-define(INVALID_NAMESPACE_ERR,
|
||||
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
|
||||
|
||||
-define(HOST_UNKNOWN_ERR,
|
||||
xml:element_to_string(?SERR_HOST_UNKNOWN)).
|
||||
|
||||
-define(INVALID_XML_ERR,
|
||||
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(From, Host, Type) ->
|
||||
supervisor:start_child(ejabberd_s2s_out_sup, [From, Host, Type]).
|
||||
|
||||
start_link(From, Host, Type) ->
|
||||
gen_fsm:start_link(ejabberd_s2s_out, [From, Host, Type], ?FSMOPTS).
|
||||
|
||||
start_connection(Pid) ->
|
||||
gen_fsm:send_event(Pid, init).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([From, Server, Type]) ->
|
||||
?INFO_MSG("started: ~p", [{From, Server, Type}]),
|
||||
TLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||
undefined ->
|
||||
false;
|
||||
UseStartTLS ->
|
||||
UseStartTLS
|
||||
end,
|
||||
UseV10 = TLS,
|
||||
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
|
||||
undefined ->
|
||||
[];
|
||||
CertFile ->
|
||||
[{certfile, CertFile}, connect]
|
||||
end,
|
||||
{New, Verify} = case Type of
|
||||
{new, Key} ->
|
||||
{Key, false};
|
||||
{verify, Pid, Key, SID} ->
|
||||
start_connection(self()),
|
||||
{false, {Pid, Key, SID}}
|
||||
end,
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, open_socket, #state{use_v10 = UseV10,
|
||||
tls = TLS,
|
||||
tls_options = TLSOpts,
|
||||
queue = queue:new(),
|
||||
myname = From,
|
||||
server = Server,
|
||||
new = New,
|
||||
verify = Verify,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
open_socket(init, StateData) ->
|
||||
AddrList = get_addr_port(StateData#state.server),
|
||||
case lists:foldl(fun({Addr, Port}, Acc) ->
|
||||
case Acc of
|
||||
{ok, Socket} ->
|
||||
{ok, Socket};
|
||||
_ ->
|
||||
open_socket1(Addr, Port)
|
||||
end
|
||||
end, {error, badarg}, AddrList) of
|
||||
{ok, Socket} ->
|
||||
ReceiverPid = ejabberd_receiver:start(Socket, gen_tcp, none),
|
||||
ok = gen_tcp:controlling_process(Socket, ReceiverPid),
|
||||
ejabberd_receiver:become_controller(ReceiverPid),
|
||||
Version = if
|
||||
StateData#state.use_v10 ->
|
||||
" version='1.0'";
|
||||
true ->
|
||||
""
|
||||
end,
|
||||
NewStateData = StateData#state{socket = Socket,
|
||||
sockmod = gen_tcp,
|
||||
tls_enabled = false,
|
||||
receiver = ReceiverPid,
|
||||
streamid = new_id()},
|
||||
send_text(NewStateData, io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.server,
|
||||
Version])),
|
||||
{next_state, wait_for_stream, NewStateData};
|
||||
{error, _Reason} ->
|
||||
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
bounce_messages(Error),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
open_socket(_, StateData) ->
|
||||
{next_state, open_socket, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
open_socket1(Addr, Port) ->
|
||||
Res = case idna:domain_utf8_to_ascii(Addr) of
|
||||
false -> {error, badarg};
|
||||
ASCIIAddr ->
|
||||
?DEBUG("s2s_out: connecting to ~s:~p~n", [ASCIIAddr, Port]),
|
||||
case catch gen_tcp:connect(ASCIIAddr, Port,
|
||||
[binary, {packet, 0},
|
||||
{active, false}]) of
|
||||
{ok, _Socket} = R -> R;
|
||||
{error, Reason1} ->
|
||||
?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
|
||||
catch gen_tcp:connect(Addr, Port,
|
||||
[binary, {packet, 0},
|
||||
{active, false}, inet6]);
|
||||
{'EXIT', Reason1} ->
|
||||
?DEBUG("s2s_out: connect crashed ~p~n", [Reason1]),
|
||||
catch gen_tcp:connect(Addr, Port,
|
||||
[binary, {packet, 0},
|
||||
{active, false}, inet6])
|
||||
end
|
||||
end,
|
||||
case Res of
|
||||
{ok, Socket} ->
|
||||
{ok, Socket};
|
||||
{error, Reason} ->
|
||||
?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
|
||||
{error, Reason};
|
||||
{'EXIT', Reason} ->
|
||||
?DEBUG("s2s_out: inet6 connect crashed ~p~n", [Reason]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
|
||||
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
case {xml:get_attr_s("xmlns", Attrs),
|
||||
xml:get_attr_s("xmlns:db", Attrs),
|
||||
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||
{"jabber:server", "jabber:server:dialback", false} ->
|
||||
send_db_request(StateData);
|
||||
{"jabber:server", "jabber:server:dialback", true} when
|
||||
StateData#state.use_v10 ->
|
||||
{next_state, wait_for_features, StateData};
|
||||
{"jabber:server", "", true} when StateData#state.use_v10 ->
|
||||
{next_state, wait_for_features, StateData#state{db_enabled = false}};
|
||||
_ ->
|
||||
send_text(StateData, ?INVALID_NAMESPACE_ERR),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_stream(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_stream(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
|
||||
wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
case is_verify_res(El) of
|
||||
{result, To, From, Id, Type} ->
|
||||
?INFO_MSG("recv result: ~p", [{From, To, Id, Type}]),
|
||||
case Type of
|
||||
"valid" ->
|
||||
send_queue(StateData, StateData#state.queue),
|
||||
{next_state, stream_established,
|
||||
StateData#state{queue = queue:new()}};
|
||||
_ ->
|
||||
% TODO: bounce packets
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
{verify, To, From, Id, Type} ->
|
||||
?INFO_MSG("recv verify: ~p", [{From, To, Id, Type}]),
|
||||
case StateData#state.verify of
|
||||
false ->
|
||||
{next_state, wait_for_validation, StateData};
|
||||
{Pid, _Key, _SID} ->
|
||||
case Type of
|
||||
"valid" ->
|
||||
gen_fsm:send_event(
|
||||
Pid, {valid,
|
||||
StateData#state.server,
|
||||
StateData#state.myname});
|
||||
_ ->
|
||||
gen_fsm:send_event(
|
||||
Pid, {invalid,
|
||||
StateData#state.server,
|
||||
StateData#state.myname})
|
||||
end,
|
||||
if
|
||||
StateData#state.verify == false ->
|
||||
{stop, normal, StateData};
|
||||
true ->
|
||||
{next_state, wait_for_validation, StateData}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{next_state, wait_for_validation, StateData}
|
||||
end;
|
||||
|
||||
wait_for_validation({xmlstreamend, Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_validation({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_validation(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_validation(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_features({xmlstreamelement, El}, StateData) ->
|
||||
case El of
|
||||
{xmlelement, "stream:features", _Attrs, Els} ->
|
||||
{SASLEXT, StartTLS, StartTLSRequired} =
|
||||
lists:foldl(
|
||||
fun({xmlelement, "mechanisms", Attrs1, Els1} = El1,
|
||||
{SEXT, STLS, STLSReq} = Acc) ->
|
||||
case xml:get_attr_s("xmlns", Attrs1) of
|
||||
?NS_SASL ->
|
||||
NewSEXT =
|
||||
lists:any(
|
||||
fun({xmlelement, "mechanism", _, Els2}) ->
|
||||
case xml:get_cdata(Els2) of
|
||||
"EXTERNAL" -> true;
|
||||
_ -> false
|
||||
end;
|
||||
(_) -> false
|
||||
end, Els1),
|
||||
{NewSEXT, STLS, STLSReq};
|
||||
_ ->
|
||||
Acc
|
||||
end;
|
||||
({xmlelement, "starttls", Attrs1, Els1} = El1,
|
||||
{SEXT, STLS, STLSReq} = Acc) ->
|
||||
case xml:get_attr_s("xmlns", Attrs1) of
|
||||
?NS_TLS ->
|
||||
Req = case xml:get_subtag(El1, "required") of
|
||||
{xmlelement, _, _, _} -> true;
|
||||
false -> false
|
||||
end,
|
||||
{SEXT, true, Req};
|
||||
_ ->
|
||||
Acc
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, {false, false, false}, Els),
|
||||
if
|
||||
(not SASLEXT) and (not StartTLS) and
|
||||
StateData#state.authenticated ->
|
||||
send_queue(StateData, StateData#state.queue),
|
||||
{next_state, stream_established,
|
||||
StateData#state{queue = queue:new()}};
|
||||
SASLEXT and StateData#state.try_auth and
|
||||
(StateData#state.new /= false) ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "auth",
|
||||
[{"xmlns", ?NS_SASL},
|
||||
{"mechanism", "EXTERNAL"}],
|
||||
[{xmlcdata,
|
||||
jlib:encode_base64(
|
||||
StateData#state.myname)}]}),
|
||||
{next_state, wait_for_auth_result,
|
||||
StateData#state{try_auth = false}};
|
||||
StartTLS and StateData#state.tls and
|
||||
(not StateData#state.tls_enabled) ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}),
|
||||
{next_state, wait_for_starttls_proceed, StateData};
|
||||
StartTLSRequired and (not StateData#state.tls) ->
|
||||
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
|
||||
StateData#state.server}]),
|
||||
ejabberd_receiver:close(StateData#state.receiver),
|
||||
{next_state, reopen_socket,
|
||||
StateData#state{socket = undefined,
|
||||
use_v10 = false}};
|
||||
StateData#state.db_enabled ->
|
||||
send_db_request(StateData);
|
||||
true ->
|
||||
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
|
||||
StateData#state.server}]),
|
||||
% TODO: clear message queue
|
||||
ejabberd_receiver:close(StateData#state.receiver),
|
||||
{next_state, reopen_socket, StateData#state{socket = undefined,
|
||||
use_v10 = false}}
|
||||
end;
|
||||
_ ->
|
||||
send_text(StateData,
|
||||
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_features({xmlstreamend, Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_features({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_features(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_features(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_auth_result({xmlstreamelement, El}, StateData) ->
|
||||
case El of
|
||||
{xmlelement, "success", Attrs, _Els} ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_SASL ->
|
||||
?INFO_MSG("auth: ~p", [{StateData#state.myname,
|
||||
StateData#state.server}]),
|
||||
ejabberd_receiver:reset_stream(
|
||||
StateData#state.receiver),
|
||||
send_text(StateData,
|
||||
io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.server,
|
||||
" version='1.0'"])),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
authenticated = true
|
||||
}};
|
||||
_ ->
|
||||
send_text(StateData,
|
||||
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
{xmlelement, "failure", Attrs, _Els} ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_SASL ->
|
||||
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
|
||||
StateData#state.server}]),
|
||||
ejabberd_receiver:close(StateData#state.receiver),
|
||||
{next_state, reopen_socket,
|
||||
StateData#state{socket = undefined}};
|
||||
_ ->
|
||||
send_text(StateData,
|
||||
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
send_text(StateData,
|
||||
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_auth_result({xmlstreamend, Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_auth_result({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_auth_result(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_auth_result(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
||||
case El of
|
||||
{xmlelement, "proceed", Attrs, _Els} ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_TLS ->
|
||||
?INFO_MSG("starttls: ~p", [{StateData#state.myname,
|
||||
StateData#state.server}]),
|
||||
Socket = StateData#state.socket,
|
||||
TLSOpts = case ejabberd_config:get_local_option(
|
||||
{domain_certfile,
|
||||
StateData#state.server}) of
|
||||
undefined ->
|
||||
StateData#state.tls_options;
|
||||
CertFile ->
|
||||
[{certfile, CertFile} |
|
||||
lists:keydelete(
|
||||
certfile, 1,
|
||||
StateData#state.tls_options)]
|
||||
end,
|
||||
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||
ejabberd_receiver:starttls(
|
||||
StateData#state.receiver, TLSSocket),
|
||||
NewStateData = StateData#state{sockmod = tls,
|
||||
socket = TLSSocket,
|
||||
streamid = new_id(),
|
||||
tls_enabled = true
|
||||
},
|
||||
send_text(NewStateData,
|
||||
io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.server,
|
||||
" version='1.0'"])),
|
||||
{next_state, wait_for_stream, NewStateData};
|
||||
_ ->
|
||||
send_text(StateData,
|
||||
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_starttls_proceed({xmlstreamend, Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_starttls_proceed({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_starttls_proceed(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_starttls_proceed(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
reopen_socket({xmlstreamelement, El}, StateData) ->
|
||||
{next_state, reopen_socket, StateData};
|
||||
reopen_socket({xmlstreamend, Name}, StateData) ->
|
||||
{next_state, reopen_socket, StateData};
|
||||
reopen_socket({xmlstreamerror, _}, StateData) ->
|
||||
{next_state, reopen_socket, StateData};
|
||||
reopen_socket(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
reopen_socket(closed, StateData) ->
|
||||
gen_fsm:send_event(self(), init),
|
||||
{next_state, open_socket, StateData}.
|
||||
|
||||
|
||||
stream_established({xmlstreamelement, El}, StateData) ->
|
||||
?INFO_MSG("stream established", []),
|
||||
case is_verify_res(El) of
|
||||
{verify, VTo, VFrom, VId, VType} ->
|
||||
?INFO_MSG("recv verify: ~p", [{VFrom, VTo, VId, VType}]),
|
||||
case StateData#state.verify of
|
||||
{VPid, _VKey, _SID} ->
|
||||
case VType of
|
||||
"valid" ->
|
||||
gen_fsm:send_event(
|
||||
VPid, {valid,
|
||||
StateData#state.server,
|
||||
StateData#state.myname});
|
||||
_ ->
|
||||
gen_fsm:send_event(
|
||||
VPid, {invalid,
|
||||
StateData#state.server,
|
||||
StateData#state.myname})
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{next_state, stream_established, StateData};
|
||||
|
||||
stream_established({xmlstreamend, Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData,
|
||||
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established(timeout, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(Event, From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData, Text),
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{next_state, StateName, StateData#state{timer = Timer}};
|
||||
|
||||
handle_info({send_element, El}, StateName, StateData) ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
case StateName of
|
||||
stream_established ->
|
||||
send_element(StateData, El),
|
||||
{next_state, StateName, StateData#state{timer = Timer}};
|
||||
_ ->
|
||||
Q = queue:in(El, StateData#state.queue),
|
||||
{next_state, StateName, StateData#state{queue = Q,
|
||||
timer = Timer}}
|
||||
end;
|
||||
|
||||
%handle_info({tcp, Socket, Data}, StateName, StateData) ->
|
||||
% xml_stream:send_text(StateData#state.xmlpid, Data),
|
||||
% {next_state, StateName, StateData};
|
||||
%
|
||||
%handle_info({tcp_closed, Socket}, StateName, StateData) ->
|
||||
% gen_fsm:send_event(self(), closed),
|
||||
% {next_state, StateName, StateData};
|
||||
%
|
||||
%handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||
% gen_fsm:send_event(self(), closed),
|
||||
% {next_state, StateName, StateData};
|
||||
|
||||
handle_info({timeout, Timer, _}, StateName,
|
||||
#state{timer = Timer} = StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
?INFO_MSG("terminated: ~p", [Reason]),
|
||||
bounce_queue(StateData#state.queue, ?ERR_REMOTE_SERVER_NOT_FOUND),
|
||||
case StateData#state.new of
|
||||
false ->
|
||||
ok;
|
||||
Key ->
|
||||
ejabberd_s2s:remove_connection({StateData#state.myname,
|
||||
StateData#state.server})
|
||||
end,
|
||||
case StateData#state.socket of
|
||||
undefined ->
|
||||
ok;
|
||||
_Socket ->
|
||||
ejabberd_receiver:close(StateData#state.receiver)
|
||||
end,
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
|
||||
send_queue(StateData, Q) ->
|
||||
case queue:out(Q) of
|
||||
{{value, El}, Q1} ->
|
||||
send_element(StateData, El),
|
||||
send_queue(StateData, Q1);
|
||||
{empty, Q1} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
bounce_queue(Q, Error) ->
|
||||
case queue:out(Q) of
|
||||
{{value, El}, Q1} ->
|
||||
Err = jlib:make_error_reply(El, Error),
|
||||
From = jlib:string_to_jid(xml:get_tag_attr_s("from", El)),
|
||||
To = jlib:string_to_jid(xml:get_tag_attr_s("to", El)),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
bounce_queue(Q1, Error);
|
||||
{empty, Q1} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
bounce_messages(Error) ->
|
||||
receive
|
||||
{send_element, El} ->
|
||||
{xmlelement, _Name, Attrs, _SubTags} = El,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(El, Error),
|
||||
From = jlib:string_to_jid(xml:get_attr_s("from", Attrs)),
|
||||
To = jlib:string_to_jid(xml:get_attr_s("to", Attrs)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end,
|
||||
bounce_messages(Error)
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
send_db_request(StateData) ->
|
||||
Server = StateData#state.server,
|
||||
New = case StateData#state.new of
|
||||
false ->
|
||||
case ejabberd_s2s:try_register(
|
||||
{StateData#state.myname, Server}) of
|
||||
{key, Key} ->
|
||||
Key;
|
||||
false ->
|
||||
false
|
||||
end;
|
||||
Key ->
|
||||
Key
|
||||
end,
|
||||
case New of
|
||||
false ->
|
||||
ok;
|
||||
Key1 ->
|
||||
send_element(StateData,
|
||||
{xmlelement,
|
||||
"db:result",
|
||||
[{"from", StateData#state.myname},
|
||||
{"to", Server}],
|
||||
[{xmlcdata, Key1}]})
|
||||
end,
|
||||
case StateData#state.verify of
|
||||
false ->
|
||||
ok;
|
||||
{Pid, Key2, SID} ->
|
||||
send_element(StateData,
|
||||
{xmlelement,
|
||||
"db:verify",
|
||||
[{"from", StateData#state.myname},
|
||||
{"to", StateData#state.server},
|
||||
{"id", SID}],
|
||||
[{xmlcdata, Key2}]})
|
||||
end,
|
||||
{next_state, wait_for_validation, StateData#state{new = New}}.
|
||||
|
||||
|
||||
is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
||||
{result,
|
||||
xml:get_attr_s("to", Attrs),
|
||||
xml:get_attr_s("from", Attrs),
|
||||
xml:get_attr_s("id", Attrs),
|
||||
xml:get_attr_s("type", Attrs)};
|
||||
is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
|
||||
{verify,
|
||||
xml:get_attr_s("to", Attrs),
|
||||
xml:get_attr_s("from", Attrs),
|
||||
xml:get_attr_s("id", Attrs),
|
||||
xml:get_attr_s("type", Attrs)};
|
||||
is_verify_res(_) ->
|
||||
false.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% SRV support
|
||||
|
||||
-include_lib("kernel/include/inet.hrl").
|
||||
|
||||
get_addr_port(Server) ->
|
||||
Res = case inet_res:getbyname("_xmpp-server._tcp." ++ Server, srv) of
|
||||
{error, _Reason} ->
|
||||
inet_res:getbyname("_jabber._tcp." ++ Server, srv);
|
||||
{ok, _HEnt} = R -> R
|
||||
end,
|
||||
case Res of
|
||||
{error, Reason} ->
|
||||
?DEBUG("srv lookup of '~s' failed: ~p~n", [Server, Reason]),
|
||||
[{Server, ejabberd_config:get_local_option(outgoing_s2s_port)}];
|
||||
{ok, HEnt} ->
|
||||
?DEBUG("srv lookup of '~s': ~p~n",
|
||||
[Server, HEnt#hostent.h_addr_list]),
|
||||
case HEnt#hostent.h_addr_list of
|
||||
[] ->
|
||||
[{Server,
|
||||
ejabberd_config:get_local_option(outgoing_s2s_port)}];
|
||||
AddrList ->
|
||||
% Probabilities are not exactly proportional to weights
|
||||
% for simplicity (higher weigths are overvalued)
|
||||
{A1, A2, A3} = now(),
|
||||
random:seed(A1, A2, A3),
|
||||
case (catch lists:map(
|
||||
fun({Priority, Weight, Port, Host}) ->
|
||||
N = case Weight of
|
||||
0 -> 0;
|
||||
_ -> (Weight + 1) * random:uniform()
|
||||
end,
|
||||
{Priority * 65536 - N, Host, Port}
|
||||
end, AddrList)) of
|
||||
{'EXIT', _Reasn} ->
|
||||
[{Server,
|
||||
ejabberd_config:get_local_option(outgoing_s2s_port)}];
|
||||
SortedList ->
|
||||
List = lists:map(
|
||||
fun({_, Host, Port}) ->
|
||||
{Host, Port}
|
||||
end, lists:keysort(1, SortedList)),
|
||||
?DEBUG("srv lookup of '~s': ~p~n", [Server, List]),
|
||||
List
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
test_get_addr_port(Server) ->
|
||||
lists:foldl(
|
||||
fun(_, Acc) ->
|
||||
[HostPort | _] = get_addr_port(Server),
|
||||
case lists:keysearch(HostPort, 1, Acc) of
|
||||
false ->
|
||||
[{HostPort, 1} | Acc];
|
||||
{value, {_, Num}} ->
|
||||
lists:keyreplace(HostPort, 1, Acc, {HostPort, Num + 1})
|
||||
end
|
||||
end, [], lists:seq(1, 100000)).
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_service.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_service).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start/2,
|
||||
start_link/2,
|
||||
send_text/2,
|
||||
send_element/2,
|
||||
become_controller/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_stream/2,
|
||||
wait_for_handshake/2,
|
||||
stream_established/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {socket, receiver, streamid, sockmod,
|
||||
hosts, password, access}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
-define(STREAM_HEADER,
|
||||
"<?xml version='1.0'?>"
|
||||
"<stream:stream "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"xmlns='jabber:component:accept' "
|
||||
"id='~s' from='~s'>"
|
||||
).
|
||||
|
||||
-define(STREAM_TRAILER, "</stream:stream>").
|
||||
|
||||
-define(INVALID_HEADER_ERR,
|
||||
"<stream:stream>"
|
||||
"<stream:error>Invalid Stream Header</stream:error>"
|
||||
"</stream:stream>"
|
||||
).
|
||||
|
||||
-define(INVALID_HANDSHAKE_ERR,
|
||||
"<stream:error>Invalid Handshake</stream:error>"
|
||||
"</stream:stream>"
|
||||
).
|
||||
|
||||
-define(INVALID_XML_ERR,
|
||||
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
|
||||
-define(INVALID_NS_ERR,
|
||||
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(SockData, Opts) ->
|
||||
supervisor:start_child(ejabberd_service_sup, [SockData, Opts]).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
gen_fsm:start_link(ejabberd_service, [SockData, Opts], ?FSMOPTS).
|
||||
|
||||
become_controller(Pid) ->
|
||||
gen_fsm:send_all_state_event(Pid, become_controller).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([{SockMod, Socket}, Opts]) ->
|
||||
Access = case lists:keysearch(access, 1, Opts) of
|
||||
{value, {_, A}} -> A;
|
||||
_ -> all
|
||||
end,
|
||||
{Hosts, Password} =
|
||||
case lists:keysearch(hosts, 1, Opts) of
|
||||
{value, {_, Hs, HOpts}} ->
|
||||
case lists:keysearch(password, 1, HOpts) of
|
||||
{value, {_, P}} ->
|
||||
{Hs, P};
|
||||
_ ->
|
||||
% TODO: generate error
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
case lists:keysearch(host, 1, Opts) of
|
||||
{value, {_, H, HOpts}} ->
|
||||
case lists:keysearch(password, 1, HOpts) of
|
||||
{value, {_, P}} ->
|
||||
{[H], P};
|
||||
_ ->
|
||||
% TODO: generate error
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
% TODO: generate error
|
||||
false
|
||||
end
|
||||
end,
|
||||
ReceiverPid = ejabberd_receiver:start(Socket, SockMod, none),
|
||||
{ok, wait_for_stream, #state{socket = Socket,
|
||||
receiver = ReceiverPid,
|
||||
streamid = new_id(),
|
||||
sockmod = SockMod,
|
||||
hosts = Hosts,
|
||||
password = Password,
|
||||
access = Access
|
||||
}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
% TODO
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
"jabber:component:accept" ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME]),
|
||||
send_text(StateData, Header),
|
||||
{next_state, wait_for_handshake, StateData};
|
||||
_ ->
|
||||
send_text(StateData, ?INVALID_HEADER_ERR),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
["none", ?MYNAME]),
|
||||
send_text(StateData,
|
||||
Header ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_stream(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_handshake({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, _Attrs, Els} = El,
|
||||
case {Name, xml:get_cdata(Els)} of
|
||||
{"handshake", Digest} ->
|
||||
case sha:sha(StateData#state.streamid ++
|
||||
StateData#state.password) of
|
||||
Digest ->
|
||||
send_text(StateData, "<handshake/>"),
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:register_route(H)
|
||||
end, StateData#state.hosts),
|
||||
{next_state, stream_established, StateData};
|
||||
_ ->
|
||||
send_text(StateData, ?INVALID_HANDSHAKE_ERR),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
{next_state, wait_for_handshake, StateData}
|
||||
end;
|
||||
|
||||
wait_for_handshake({xmlstreamend, _Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_handshake({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_handshake(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
stream_established({xmlstreamelement, El}, StateData) ->
|
||||
NewEl = jlib:remove_attr("xmlns", El),
|
||||
{xmlelement, Name, Attrs, _Els} = NewEl,
|
||||
From = xml:get_attr_s("from", Attrs),
|
||||
FromJID1 = jlib:string_to_jid(From),
|
||||
FromJID = case FromJID1 of
|
||||
#jid{lserver = Server} ->
|
||||
case lists:member(Server, StateData#state.hosts) of
|
||||
true -> FromJID1;
|
||||
false -> 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, NewEl);
|
||||
true ->
|
||||
Err = jlib:make_error_reply(NewEl, ?ERR_BAD_REQUEST),
|
||||
send_element(StateData, Err),
|
||||
error
|
||||
end,
|
||||
{next_state, stream_established, StateData};
|
||||
|
||||
stream_established({xmlstreamend, _Name}, StateData) ->
|
||||
% TODO
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
stream_established(closed, StateData) ->
|
||||
% TODO
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(become_controller, StateName, StateData) ->
|
||||
ok = (StateData#state.sockmod):controlling_process(
|
||||
StateData#state.socket,
|
||||
StateData#state.receiver),
|
||||
ejabberd_receiver:become_controller(StateData#state.receiver),
|
||||
{next_state, StateName, StateData};
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData, Text),
|
||||
{next_state, StateName, StateData};
|
||||
handle_info({send_element, El}, StateName, StateData) ->
|
||||
send_element(StateData, El),
|
||||
{next_state, StateName, StateData};
|
||||
handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
case acl:match_rule(global, StateData#state.access, From) of
|
||||
allow ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
send_text(StateData, Text);
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end,
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
?INFO_MSG("terminated: ~p", [Reason]),
|
||||
case StateName of
|
||||
stream_established ->
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:unregister_route(H)
|
||||
end, StateData#state.hosts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_receiver:close(StateData#state.receiver),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket,Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
|
||||
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
|
@ -1,617 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_sm.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Session manager
|
||||
%%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_sm).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0,
|
||||
route/3,
|
||||
open_session/4, close_session/4,
|
||||
bounce_offline_message/3,
|
||||
disconnect_removed_user/2,
|
||||
get_user_resources/2,
|
||||
set_presence/5,
|
||||
unset_presence/5,
|
||||
close_session_unset_presence/5,
|
||||
dirty_get_sessions_list/0,
|
||||
dirty_get_my_sessions_list/0,
|
||||
get_vh_session_list/1,
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
ctl_process/2
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_ctl.hrl").
|
||||
|
||||
-record(session, {sid, usr, us, priority}).
|
||||
-record(state, {}).
|
||||
|
||||
%% default value for the maximum number of user connections
|
||||
-define(MAX_USER_SESSIONS, infinity).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
open_session(SID, User, Server, Resource) ->
|
||||
set_session(SID, User, Server, Resource, undefined),
|
||||
check_for_sessions_to_replace(User, Server, Resource),
|
||||
JID = jlib:make_jid(User, Server, Resource),
|
||||
ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver,
|
||||
[SID, JID]).
|
||||
|
||||
close_session(SID, User, Server, Resource) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({session, SID})
|
||||
end,
|
||||
mnesia:sync_dirty(F),
|
||||
JID = jlib:make_jid(User, Server, Resource),
|
||||
ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver,
|
||||
[SID, JID]).
|
||||
|
||||
bounce_offline_message(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
|
||||
disconnect_removed_user(User, Server) ->
|
||||
ejabberd_sm:route(jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, Server, ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}).
|
||||
|
||||
get_user_resources(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(session, US, #session.us) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
Ss ->
|
||||
[element(3, S#session.usr) || S <- clean_session_list(Ss)]
|
||||
end.
|
||||
|
||||
set_presence(SID, User, Server, Resource, Priority) ->
|
||||
set_session(SID, User, Server, Resource, Priority).
|
||||
|
||||
unset_presence(SID, User, Server, Resource, Status) ->
|
||||
set_session(SID, User, Server, Resource, undefined),
|
||||
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
|
||||
[User, Server, Resource, Status]).
|
||||
|
||||
close_session_unset_presence(SID, User, Server, Resource, Status) ->
|
||||
close_session(SID, User, Server, Resource),
|
||||
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
|
||||
[User, Server, Resource, Status]).
|
||||
|
||||
|
||||
dirty_get_sessions_list() ->
|
||||
mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{usr = '$1', _ = '_'},
|
||||
[],
|
||||
['$1']}]).
|
||||
|
||||
dirty_get_my_sessions_list() ->
|
||||
mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{sid = {'_', '$1'}, _ = '_'},
|
||||
[{'==', {node, '$1'}, node()}],
|
||||
['$_']}]).
|
||||
|
||||
get_vh_session_list(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{usr = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]).
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
|
||||
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}.
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
update_tables(),
|
||||
mnesia:create_table(session,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, session)}]),
|
||||
mnesia:add_table_index(session, usr),
|
||||
mnesia:add_table_index(session, us),
|
||||
mnesia:add_table_copy(session, node(), ram_copies),
|
||||
mnesia:subscribe(system),
|
||||
ets:new(sm_iqtable, [named_table]),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_hooks:add(offline_message_hook, Host,
|
||||
ejabberd_sm, bounce_offline_message, 100),
|
||||
ejabberd_hooks:add(remove_user, Host,
|
||||
ejabberd_sm, disconnect_removed_user, 100)
|
||||
end, ?MYHOSTS),
|
||||
ejabberd_ctl:register_commands(
|
||||
[{"connected-users", "list all established sessions"},
|
||||
{"connected-users-number", "print a number of established sessions"},
|
||||
{"user-resources user server", "print user's connected resources"}],
|
||||
?MODULE, ctl_process),
|
||||
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({route, From, To, Packet}, State) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
clean_table_from_bad_node(Node),
|
||||
{noreply, State};
|
||||
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
|
||||
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}),
|
||||
{noreply, State};
|
||||
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
|
||||
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}),
|
||||
{noreply, State};
|
||||
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
|
||||
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(sm_iqtable, {XMLNS, Host}),
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
set_session(SID, User, Server, Resource, Priority) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
US = {LUser, LServer},
|
||||
USR = {LUser, LServer, LResource},
|
||||
F = fun() ->
|
||||
mnesia:write(#session{sid = SID,
|
||||
usr = USR,
|
||||
us = US,
|
||||
priority = Priority})
|
||||
end,
|
||||
mnesia:sync_dirty(F).
|
||||
|
||||
clean_table_from_bad_node(Node) ->
|
||||
F = fun() ->
|
||||
Es = mnesia:select(
|
||||
session,
|
||||
[{#session{sid = {'_', '$1'}, _ = '_'},
|
||||
[{'==', {node, '$1'}, Node}],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete({session, E#session.sid})
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:sync_dirty(F).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer, lresource = LResource} = To,
|
||||
{xmlelement, Name, Attrs, _Els} = Packet,
|
||||
case LResource of
|
||||
"" ->
|
||||
case Name of
|
||||
"presence" ->
|
||||
{Pass, Subsc} =
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"subscribe" ->
|
||||
Reason = xml:get_path_s(
|
||||
Packet,
|
||||
[{elem, "status"}, cdata]),
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, subscribe, Reason]),
|
||||
true};
|
||||
"subscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, subscribed, ""]),
|
||||
true};
|
||||
"unsubscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, unsubscribe, ""]),
|
||||
true};
|
||||
"unsubscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, unsubscribed, ""]),
|
||||
true};
|
||||
_ ->
|
||||
{true, false}
|
||||
end,
|
||||
if Pass ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
PResources = get_user_present_resources(
|
||||
LUser, LServer),
|
||||
lists:foreach(
|
||||
fun({_, R}) ->
|
||||
if LFrom /=
|
||||
{LUser, LServer, R} ->
|
||||
do_route(
|
||||
From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, PResources);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
"message" ->
|
||||
route_message(From, To, Packet);
|
||||
"iq" ->
|
||||
process_iq(From, To, Packet);
|
||||
"broadcast" ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
do_route(From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet)
|
||||
end, get_user_resources(User, Server));
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
USR = {LUser, LServer, LResource},
|
||||
case mnesia:dirty_index_read(session, USR, #session.usr) of
|
||||
[] ->
|
||||
case Name of
|
||||
"message" ->
|
||||
route_message(From, To, Packet);
|
||||
"iq" ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"error" -> ok;
|
||||
"result" -> ok;
|
||||
_ ->
|
||||
Err =
|
||||
jlib:make_error_reply(
|
||||
Packet, ?ERR_RECIPIENT_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
_ ->
|
||||
?DEBUG("packet droped~n", [])
|
||||
end;
|
||||
Ss ->
|
||||
Session = lists:max(Ss),
|
||||
Pid = element(2, Session#session.sid),
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet}
|
||||
end
|
||||
end.
|
||||
|
||||
route_message(From, To, Packet) ->
|
||||
LUser = To#jid.luser,
|
||||
LServer = To#jid.lserver,
|
||||
PrioRes = get_user_present_resources(LUser, LServer),
|
||||
case catch lists:max(PrioRes) of
|
||||
{Priority, _R} when is_integer(Priority), Priority >= 0 ->
|
||||
lists:foreach(
|
||||
%% Route messages to all priority that equals the max, if
|
||||
%% positive
|
||||
fun({P, R}) when P == Priority ->
|
||||
LResource = jlib:resourceprep(R),
|
||||
USR = {LUser, LServer, LResource},
|
||||
case mnesia:dirty_index_read(session, USR, #session.usr) of
|
||||
[] ->
|
||||
ok; % Race condition
|
||||
Ss ->
|
||||
Session = lists:max(Ss),
|
||||
Pid = element(2, Session#session.sid),
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet}
|
||||
end;
|
||||
%% Ignore other priority:
|
||||
({_Prio, _Res}) ->
|
||||
ok
|
||||
end,
|
||||
PrioRes);
|
||||
_ ->
|
||||
case xml:get_tag_attr_s("type", Packet) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
LServer,
|
||||
[From, To, Packet]);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
clean_session_list(Ss) ->
|
||||
clean_session_list(lists:keysort(#session.usr, Ss), []).
|
||||
|
||||
clean_session_list([], Res) ->
|
||||
Res;
|
||||
clean_session_list([S], Res) ->
|
||||
[S | Res];
|
||||
clean_session_list([S1, S2 | Rest], Res) ->
|
||||
if
|
||||
S1#session.usr == S2#session.usr ->
|
||||
if
|
||||
S1#session.sid > S2#session.sid ->
|
||||
clean_session_list([S1 | Rest], Res);
|
||||
true ->
|
||||
clean_session_list([S2 | Rest], Res)
|
||||
end;
|
||||
true ->
|
||||
clean_session_list([S2 | Rest], [S1 | Res])
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
get_user_present_resources(LUser, LServer) ->
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(session, US, #session.us) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
Ss ->
|
||||
[{S#session.priority, element(3, S#session.usr)} ||
|
||||
S <- clean_session_list(Ss), is_integer(S#session.priority)]
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%% On new session, check if some existing connections need to be replace
|
||||
check_for_sessions_to_replace(User, Server, Resource) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
|
||||
%% TODO: Depending on how this is executed, there could be an unneeded
|
||||
%% replacement for max_sessions. We need to check this at some point.
|
||||
check_existing_resources(LUser, LServer, LResource),
|
||||
check_max_sessions(LUser, LServer).
|
||||
|
||||
check_existing_resources(LUser, LServer, LResource) ->
|
||||
USR = {LUser, LServer, LResource},
|
||||
%% A connection exist with the same resource. We replace it:
|
||||
SIDs = mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]),
|
||||
if
|
||||
SIDs == [] -> ok;
|
||||
true ->
|
||||
MaxSID = lists:max(SIDs),
|
||||
lists:foreach(
|
||||
fun({_, Pid} = S) when S /= MaxSID ->
|
||||
Pid ! replaced;
|
||||
(_) -> ok
|
||||
end, SIDs)
|
||||
end.
|
||||
|
||||
check_max_sessions(LUser, LServer) ->
|
||||
%% If the max number of sessions for a given is reached, we replace the
|
||||
%% first one
|
||||
SIDs = mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{sid = '$1', usr = {LUser, LServer, '_'}, _ = '_'}, [], ['$1']}]),
|
||||
MaxSessions = get_max_user_sessions(LServer),
|
||||
if length(SIDs) =< MaxSessions -> ok;
|
||||
true -> {_, Pid} = lists:min(SIDs),
|
||||
Pid ! replaced
|
||||
end.
|
||||
|
||||
|
||||
%% Get the user_max_session setting
|
||||
%% This option defines the max number of time a given users are allowed to
|
||||
%% log in
|
||||
%% Defaults to infinity
|
||||
get_max_user_sessions(Host) ->
|
||||
case ejabberd_config:get_local_option({max_user_sessions, Host}) of
|
||||
undefined -> ?MAX_USER_SESSIONS;
|
||||
Max -> Max
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
process_iq(From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{xmlns = XMLNS} ->
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
ResIQ /= ignore ->
|
||||
ejabberd_router:route(To, From,
|
||||
jlib:iq_to_xml(ResIQ));
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
reply ->
|
||||
ok;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
ctl_process(_Val, ["connected-users"]) ->
|
||||
USRs = dirty_get_sessions_list(),
|
||||
NewLine = io_lib:format("~n", []),
|
||||
SUSRs = lists:sort(USRs),
|
||||
FUSRs = lists:map(fun({U, S, R}) -> [U, $@, S, $/, R, NewLine] end, SUSRs),
|
||||
io:format("~s", [FUSRs]),
|
||||
{stop, ?STATUS_SUCCESS};
|
||||
ctl_process(_Val, ["connected-users-number"]) ->
|
||||
N = length(dirty_get_sessions_list()),
|
||||
io:format("~p~n", [N]),
|
||||
{stop, ?STATUS_SUCCESS};
|
||||
ctl_process(_Val, ["user-resources", User, Server]) ->
|
||||
Resources = get_user_resources(User, Server),
|
||||
NewLine = io_lib:format("~n", []),
|
||||
SResources = lists:sort(Resources),
|
||||
FResources = lists:map(fun(R) -> [R, NewLine] end, SResources),
|
||||
io:format("~s", [FResources]),
|
||||
{stop, ?STATUS_SUCCESS};
|
||||
ctl_process(Val, _Args) ->
|
||||
Val.
|
||||
|
||||
|
||||
update_tables() ->
|
||||
case catch mnesia:table_info(session, attributes) of
|
||||
[ur, user, node] ->
|
||||
mnesia:delete_table(session);
|
||||
[ur, user, pid] ->
|
||||
mnesia:delete_table(session);
|
||||
[usr, us, pid] ->
|
||||
mnesia:delete_table(session);
|
||||
[sid, usr, us, priority] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(presence, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(presence);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
case lists:member(local_session, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_session);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_sup.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_sup).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0, init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
|
||||
init([]) ->
|
||||
Hooks =
|
||||
{ejabberd_hooks,
|
||||
{ejabberd_hooks, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_hooks]},
|
||||
StringPrep =
|
||||
{stringprep,
|
||||
{stringprep, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[stringprep]},
|
||||
Router =
|
||||
{ejabberd_router,
|
||||
{ejabberd_router, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_router]},
|
||||
SM =
|
||||
{ejabberd_sm,
|
||||
{ejabberd_sm, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_sm]},
|
||||
S2S =
|
||||
{ejabberd_s2s,
|
||||
{ejabberd_s2s, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_s2s]},
|
||||
Local =
|
||||
{ejabberd_local,
|
||||
{ejabberd_local, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_local]},
|
||||
Listener =
|
||||
{ejabberd_listener,
|
||||
{ejabberd_listener, start_link, []},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_listener]},
|
||||
ReceiverSupervisor =
|
||||
{ejabberd_receiver_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_receiver_sup, ejabberd_receiver]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
C2SSupervisor =
|
||||
{ejabberd_c2s_sup,
|
||||
{ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
S2SInSupervisor =
|
||||
{ejabberd_s2s_in_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_s2s_in_sup, ejabberd_s2s_in]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
S2SOutSupervisor =
|
||||
{ejabberd_s2s_out_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_s2s_out_sup, ejabberd_s2s_out]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
ServiceSupervisor =
|
||||
{ejabberd_service_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_service_sup, ejabberd_service]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPSupervisor =
|
||||
{ejabberd_http_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_http_sup, ejabberd_http]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPPollSupervisor =
|
||||
{ejabberd_http_poll_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_http_poll_sup, ejabberd_http_poll]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
IQSupervisor =
|
||||
{ejabberd_iq_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_iq_sup, gen_iq_handler]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Hooks,
|
||||
StringPrep,
|
||||
Router,
|
||||
SM,
|
||||
S2S,
|
||||
Local,
|
||||
ReceiverSupervisor,
|
||||
C2SSupervisor,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
ServiceSupervisor,
|
||||
HTTPSupervisor,
|
||||
HTTPPollSupervisor,
|
||||
IQSupervisor,
|
||||
Listener]}}.
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_tmp_sup.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Supervisor for temporary processess
|
||||
%%% Created : 18 Jul 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_tmp_sup).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start_link/2, init/1]).
|
||||
|
||||
start_link(Name, Module) ->
|
||||
supervisor:start_link({local, Name}, ?MODULE, Module).
|
||||
|
||||
|
||||
init(Module) ->
|
||||
{ok, {{simple_one_for_one, 10, 1},
|
||||
[{undefined, {Module, start_link, []},
|
||||
temporary, brutal_kill, worker, [Module]}]}}.
|
|
@ -1,128 +0,0 @@
|
|||
%%%-------------------------------------------------------------------
|
||||
%%% File : ejabberd_update.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Update ejabberd
|
||||
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id: ejabberd_c2s.erl 492 2006-01-25 00:35:12Z alexey $
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_update).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
%% API
|
||||
-export([update/0, update_info/0]).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
update() ->
|
||||
case update_info() of
|
||||
{ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} ->
|
||||
Eval =
|
||||
release_handler_1:eval_script(
|
||||
LowLevelScript, [],
|
||||
[{ejabberd, "", filename:join(Dir, "..")}]),
|
||||
io:format("eval: ~p~n", [Eval]),
|
||||
Eval;
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
update_info() ->
|
||||
Dir = filename:dirname(code:which(ejabberd)),
|
||||
case file:list_dir(Dir) of
|
||||
{ok, Files} ->
|
||||
Beams = [list_to_atom(filename:rootname(FN)) ||
|
||||
FN <- Files, lists:suffix(".beam", FN)],
|
||||
UpdatedBeams =
|
||||
lists:filter(
|
||||
fun(Module) ->
|
||||
{ok, {Module, [NewVsn]}} =
|
||||
beam_lib:version(code:which(Module)),
|
||||
case code:is_loaded(Module) of
|
||||
{file, _} ->
|
||||
Attrs = Module:module_info(attributes),
|
||||
{value, {vsn, [CurVsn]}} =
|
||||
lists:keysearch(vsn, 1, Attrs),
|
||||
NewVsn /= CurVsn;
|
||||
false ->
|
||||
false
|
||||
end
|
||||
end, Beams),
|
||||
io:format("beam files: ~p~n", [UpdatedBeams]),
|
||||
Script = make_script(UpdatedBeams),
|
||||
io:format("script: ~p~n", [Script]),
|
||||
LowLevelScript = make_low_level_script(UpdatedBeams, Script),
|
||||
io:format("low level script: ~p~n", [LowLevelScript]),
|
||||
Check =
|
||||
release_handler_1:check_script(
|
||||
LowLevelScript,
|
||||
[{ejabberd, "", filename:join(Dir, "..")}]),
|
||||
io:format("check: ~p~n", [Check]),
|
||||
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check};
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% From systools.hrl
|
||||
-record(application,
|
||||
{name, %% Name of the application, atom().
|
||||
type = permanent, %% Application start type, atom().
|
||||
vsn = "", %% Version of the application, string().
|
||||
id = "", %% Id of the application, string().
|
||||
description = "", %% Description of application, string().
|
||||
modules = [], %% [Module | {Module,Vsn}] of modules
|
||||
%% incorporated in the application,
|
||||
%% Module = atom(), Vsn = string().
|
||||
uses = [], %% [Application] list of applications required
|
||||
%% by the application, Application = atom().
|
||||
includes = [], %% [Application] list of applications included
|
||||
%% by the application, Application = atom().
|
||||
regs = [], %% [RegNames] a list of registered process
|
||||
%% names used by the application, RegNames =
|
||||
%% atom().
|
||||
env = [], %% [{Key,Value}] environment variable of
|
||||
%% application, Key = Value = term().
|
||||
maxT = infinity, %% Max time an application may exist,
|
||||
%% integer() | infinity.
|
||||
maxP = infinity, %% Max number of processes in an application,
|
||||
%% integer() | infinity.
|
||||
mod = [], %% [] | {Mod, StartArgs}, Mod= atom(),
|
||||
%% StartArgs = list().
|
||||
start_phases = [], %% [] | {Phase, PhaseArgs}, Phase = atom(),
|
||||
%% PhaseArgs = list().
|
||||
dir = "" %% The directory where the .app file was
|
||||
%% found (internal use).
|
||||
}).
|
||||
|
||||
|
||||
make_script(UpdatedBeams) ->
|
||||
lists:map(
|
||||
fun(Module) ->
|
||||
{ok, {Module, [{attributes, NewAttrs}]}} =
|
||||
beam_lib:chunks(code:which(Module), [attributes]),
|
||||
CurAttrs = Module:module_info(attributes),
|
||||
case lists:keysearch(update_info, 1, NewAttrs) of
|
||||
{value, {_, [{update, _}]}} ->
|
||||
case lists:keysearch(update_info, 1, CurAttrs) of
|
||||
{value, {_, [{update, Extra}]}} ->
|
||||
{update, Module, {advanced, Extra}};
|
||||
false ->
|
||||
{update, Module, {advanced, 0}}
|
||||
end;
|
||||
false ->
|
||||
{load_module, Module}
|
||||
end
|
||||
end, UpdatedBeams).
|
||||
|
||||
make_low_level_script(UpdatedBeams, Script) ->
|
||||
EJDApp = #application{name = ejabberd,
|
||||
modules = UpdatedBeams},
|
||||
{ok, LowLevelScript} =
|
||||
systools_rc:translate_scripts([Script], [EJDApp], [EJDApp]),
|
||||
LowLevelScript.
|
|
@ -1,45 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ZLIB_CFLAGS@ @ERLANG_CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @ZLIB_LIBS@ @ERLANG_LIBS@
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress
|
||||
else
|
||||
# Assume Linux-style dynamic library flags
|
||||
DYNAMIC_LIB_CFLAGS = -fpic -shared
|
||||
endif
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
ERLSHLIBS = ../ejabberd_zlib_drv.so
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
$(OUTDIR)/ejabberd_zlib.beam
|
||||
|
||||
all: $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
#all: $(ERLSHLIBS)
|
||||
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
||||
|
||||
$(ERLSHLIBS): ../%.so: %.c
|
||||
$(CC) -Wall $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
|
||||
-o $@ $(DYNAMIC_LIB_CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
|
@ -1,37 +0,0 @@
|
|||
|
||||
include ..\Makefile.inc
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OBJS = \
|
||||
$(OUTDIR)\ejabberd_zlib.beam
|
||||
|
||||
SOURCE = ejabberd_zlib_drv.c
|
||||
OBJECT = ejabberd_zlib_drv.o
|
||||
DLL = $(OUTDIR)\ejabberd_zlib_drv.dll
|
||||
|
||||
ALL : $(DLL) $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase $(DLL)
|
||||
-@erase $(OUTDIR)\ejabberd_zlib_drv.exp
|
||||
-@erase $(OUTDIR)\ejabberd_zlib_drv.lib
|
||||
-@erase $(OBJECT)
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\ejabberd_zlib.beam : ejabberd_zlib.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_zlib.erl
|
||||
|
||||
CC=cl.exe
|
||||
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(ZLIB_DIR)\include"
|
||||
|
||||
LD=link.exe
|
||||
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(ZLIB_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
|
||||
|
||||
$(DLL) : $(OBJECT)
|
||||
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
|
||||
|
||||
$(OBJECT) : $(SOURCE)
|
||||
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_zlib.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Interface to zlib
|
||||
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_zlib).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start/0, start_link/0,
|
||||
enable_zlib/2, disable_zlib/1,
|
||||
send/2,
|
||||
recv/2, recv/3, recv_data/2,
|
||||
setopts/2,
|
||||
controlling_process/2,
|
||||
close/1]).
|
||||
|
||||
%% Internal exports, call-back functions.
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
code_change/3,
|
||||
terminate/2]).
|
||||
|
||||
-define(DEFLATE, 1).
|
||||
-define(INFLATE, 2).
|
||||
|
||||
-record(zlibsock, {sockmod, socket, zlibport}).
|
||||
|
||||
start() ->
|
||||
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
|
||||
ok -> ok;
|
||||
{error, already_loaded} -> ok
|
||||
end,
|
||||
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
|
||||
{ok, Port}.
|
||||
|
||||
|
||||
%%% --------------------------------------------------------
|
||||
%%% The call-back functions.
|
||||
%%% --------------------------------------------------------
|
||||
|
||||
handle_call(_, _, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_cast(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({'EXIT', Port, Reason}, Port) ->
|
||||
{stop, {port_died, Reason}, Port};
|
||||
|
||||
handle_info({'EXIT', _Pid, _Reason}, Port) ->
|
||||
{noreply, Port};
|
||||
|
||||
handle_info(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
terminate(_Reason, Port) ->
|
||||
Port ! {self, close},
|
||||
ok.
|
||||
|
||||
|
||||
enable_zlib(SockMod, Socket) ->
|
||||
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
|
||||
ok -> ok;
|
||||
{error, already_loaded} -> ok
|
||||
end,
|
||||
Port = open_port({spawn, ejabberd_zlib_drv}, [binary]),
|
||||
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}.
|
||||
|
||||
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
|
||||
port_close(Port),
|
||||
{SockMod, Socket}.
|
||||
|
||||
recv(Socket, Length) ->
|
||||
recv(Socket, Length, infinity).
|
||||
recv(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock,
|
||||
Length, Timeout) ->
|
||||
case SockMod:recv(Socket, Length, Timeout) of
|
||||
{ok, Packet} ->
|
||||
recv_data(ZlibSock, Packet);
|
||||
{error, _Reason} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
recv_data(#zlibsock{zlibport = Port} = _ZlibSock, Packet) ->
|
||||
case port_control(Port, ?INFLATE, Packet) of
|
||||
<<0, In/binary>> ->
|
||||
{ok, In};
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end.
|
||||
|
||||
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
|
||||
Packet) ->
|
||||
case port_control(Port, ?DEFLATE, Packet) of
|
||||
<<0, Out/binary>> ->
|
||||
SockMod:send(Socket, Out);
|
||||
<<1, Error/binary>> ->
|
||||
{error, binary_to_list(Error)}
|
||||
end.
|
||||
|
||||
|
||||
setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) ->
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket, Opts);
|
||||
_ ->
|
||||
SockMod:setopts(Socket, Opts)
|
||||
end.
|
||||
|
||||
controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) ->
|
||||
SockMod:controlling_process(Socket, Pid).
|
||||
|
||||
close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
|
||||
SockMod:close(Socket),
|
||||
port_close(Port).
|
||||
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <erl_driver.h>
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
ErlDrvPort port;
|
||||
z_stream *d_stream;
|
||||
z_stream *i_stream;
|
||||
} ejabberd_zlib_data;
|
||||
|
||||
|
||||
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
|
||||
{
|
||||
ejabberd_zlib_data *d =
|
||||
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
|
||||
d->port = port;
|
||||
|
||||
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
|
||||
|
||||
d->d_stream->zalloc = (alloc_func)0;
|
||||
d->d_stream->zfree = (free_func)0;
|
||||
d->d_stream->opaque = (voidpf)0;
|
||||
|
||||
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
|
||||
|
||||
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
|
||||
|
||||
d->i_stream->zalloc = (alloc_func)0;
|
||||
d->i_stream->zfree = (free_func)0;
|
||||
d->i_stream->opaque = (voidpf)0;
|
||||
|
||||
inflateInit(d->i_stream);
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
|
||||
return (ErlDrvData)d;
|
||||
}
|
||||
|
||||
static void ejabberd_zlib_drv_stop(ErlDrvData handle)
|
||||
{
|
||||
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
|
||||
|
||||
deflateEnd(d->d_stream);
|
||||
free(d->d_stream);
|
||||
|
||||
inflateEnd(d->i_stream);
|
||||
free(d->i_stream);
|
||||
|
||||
driver_free((char *)handle);
|
||||
}
|
||||
|
||||
|
||||
#define DEFLATE 1
|
||||
#define INFLATE 2
|
||||
|
||||
#define die_unless(cond, errstr) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
rlen = strlen(errstr) + 1; \
|
||||
b = driver_realloc_binary(b, rlen); \
|
||||
b->orig_bytes[0] = 1; \
|
||||
strncpy(b->orig_bytes + 1, errstr, rlen - 1); \
|
||||
*rbuf = (char *)b; \
|
||||
return rlen; \
|
||||
}
|
||||
|
||||
|
||||
static int ejabberd_zlib_drv_control(ErlDrvData handle,
|
||||
unsigned int command,
|
||||
char *buf, int len,
|
||||
char **rbuf, int rlen)
|
||||
{
|
||||
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
|
||||
int err;
|
||||
int size;
|
||||
ErlDrvBinary *b;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case DEFLATE:
|
||||
size = BUF_SIZE + 1;
|
||||
rlen = 1;
|
||||
b = driver_alloc_binary(size);
|
||||
b->orig_bytes[0] = 0;
|
||||
|
||||
d->d_stream->next_in = buf;
|
||||
d->d_stream->avail_in = len;
|
||||
d->d_stream->avail_out = 0;
|
||||
err = Z_OK;
|
||||
|
||||
while (err == Z_OK && d->d_stream->avail_out == 0)
|
||||
{
|
||||
d->d_stream->next_out = b->orig_bytes + rlen;
|
||||
d->d_stream->avail_out = BUF_SIZE;
|
||||
|
||||
err = deflate(d->d_stream, Z_SYNC_FLUSH);
|
||||
die_unless((err == Z_OK) || (err == Z_STREAM_END),
|
||||
"Deflate error");
|
||||
|
||||
rlen += (BUF_SIZE - d->d_stream->avail_out);
|
||||
size += (BUF_SIZE - d->d_stream->avail_out);
|
||||
b = driver_realloc_binary(b, size);
|
||||
}
|
||||
b = driver_realloc_binary(b, rlen);
|
||||
*rbuf = (char *)b;
|
||||
return rlen;
|
||||
case INFLATE:
|
||||
size = BUF_SIZE + 1;
|
||||
rlen = 1;
|
||||
b = driver_alloc_binary(size);
|
||||
b->orig_bytes[0] = 0;
|
||||
|
||||
if (len > 0) {
|
||||
d->i_stream->next_in = buf;
|
||||
d->i_stream->avail_in = len;
|
||||
d->i_stream->avail_out = 0;
|
||||
err = Z_OK;
|
||||
|
||||
while (err == Z_OK && d->i_stream->avail_out == 0)
|
||||
{
|
||||
d->i_stream->next_out = b->orig_bytes + rlen;
|
||||
d->i_stream->avail_out = BUF_SIZE;
|
||||
|
||||
err = inflate(d->i_stream, Z_SYNC_FLUSH);
|
||||
die_unless((err == Z_OK) || (err == Z_STREAM_END),
|
||||
"Inflate error");
|
||||
|
||||
rlen += (BUF_SIZE - d->i_stream->avail_out);
|
||||
size += (BUF_SIZE - d->i_stream->avail_out);
|
||||
b = driver_realloc_binary(b, size);
|
||||
}
|
||||
}
|
||||
b = driver_realloc_binary(b, rlen);
|
||||
*rbuf = (char *)b;
|
||||
return rlen;
|
||||
}
|
||||
|
||||
b = driver_alloc_binary(1);
|
||||
b->orig_bytes[0] = 0;
|
||||
*rbuf = (char *)b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ErlDrvEntry ejabberd_zlib_driver_entry = {
|
||||
NULL, /* F_PTR init, N/A */
|
||||
ejabberd_zlib_drv_start, /* L_PTR start, called when port is opened */
|
||||
ejabberd_zlib_drv_stop, /* F_PTR stop, called when port is closed */
|
||||
NULL, /* 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 */
|
||||
"ejabberd_zlib_drv", /* char *driver_name, the argument to open_port */
|
||||
NULL, /* F_PTR finish, called when unloaded */
|
||||
NULL, /* handle */
|
||||
ejabberd_zlib_drv_control, /* F_PTR control, port_command callback */
|
||||
NULL, /* F_PTR timeout, reserved */
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
};
|
||||
|
||||
DRIVER_INIT(ejabberd_zlib_drv) /* must match name in driver_entry */
|
||||
{
|
||||
return &ejabberd_zlib_driver_entry;
|
||||
}
|
||||
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejd2odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Export some mnesia tables to SQL DB
|
||||
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejd2odbc).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([export_passwd/2,
|
||||
export_roster/2,
|
||||
export_offline/2,
|
||||
export_last/2,
|
||||
export_vcard/2,
|
||||
export_vcard_search/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-record(offline_msg, {us, timestamp, expire, from, to, packet}).
|
||||
-record(last_activity, {us, timestamp, status}).
|
||||
-record(vcard, {us, vcard}).
|
||||
-record(vcard_search, {us,
|
||||
user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
middle, lmiddle,
|
||||
nickname, lnickname,
|
||||
bday, lbday,
|
||||
ctry, lctry,
|
||||
locality, llocality,
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit
|
||||
}).
|
||||
|
||||
-define(MAX_RECORDS_PER_TRANSACTION, 1000).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
export_passwd(Server, Output) ->
|
||||
export_common(
|
||||
Server, passwd, Output,
|
||||
fun(Host, {passwd, {LUser, LServer}, Password} = R)
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
["delete from users where username='", Username ,"';"
|
||||
"insert into users(username, password) "
|
||||
"values ('", Username, "', '", Pass, "');"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end).
|
||||
|
||||
export_roster(Server, Output) ->
|
||||
export_common(
|
||||
Server, roster, Output,
|
||||
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
|
||||
ItemVals = record_to_string(R),
|
||||
ItemGroups = groups_to_string(R),
|
||||
["delete from rosterusers "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';"
|
||||
"insert into rosterusers("
|
||||
" username, jid, nick, "
|
||||
" subscription, ask, askmessage, "
|
||||
" server, subscribe, type) "
|
||||
" values ", ItemVals, ";"
|
||||
"delete from rostergroups "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';",
|
||||
[["insert into rostergroups("
|
||||
" username, jid, grp) "
|
||||
" values ", ItemGroup, ";"] ||
|
||||
ItemGroup <- ItemGroups]];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end).
|
||||
|
||||
export_offline(Server, Output) ->
|
||||
export_common(
|
||||
Server, offline_msg, Output,
|
||||
fun(Host, #offline_msg{us = {LUser, LServer},
|
||||
timestamp = TimeStamp,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet})
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
Attrs2 = jlib:replace_from_to_attrs(
|
||||
jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
NewPacket = {xmlelement, Name, Attrs2,
|
||||
Els ++
|
||||
[jlib:timestamp_to_xml(
|
||||
calendar:now_to_universal_time(TimeStamp))]},
|
||||
XML =
|
||||
ejabberd_odbc:escape(
|
||||
lists:flatten(
|
||||
xml:element_to_string(NewPacket))),
|
||||
["insert into spool(username, xml) "
|
||||
"values ('", Username, "', '",
|
||||
XML,
|
||||
"');"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end).
|
||||
|
||||
export_last(Server, Output) ->
|
||||
export_common(
|
||||
Server, last_activity, Output,
|
||||
fun(Host, #last_activity{us = {LUser, LServer},
|
||||
timestamp = TimeStamp,
|
||||
status = Status})
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
|
||||
State = ejabberd_odbc:escape(Status),
|
||||
["delete from last where username='", Username, "';"
|
||||
"insert into last(username, seconds, state) "
|
||||
"values ('", Username, "', '", Seconds, "', '", State, "');"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end).
|
||||
|
||||
export_vcard(Server, Output) ->
|
||||
export_common(
|
||||
Server, vcard, Output,
|
||||
fun(Host, #vcard{us = {LUser, LServer},
|
||||
vcard = VCARD})
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
SVCARD = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(VCARD))),
|
||||
["delete from vcard where username='", Username, "';"
|
||||
"insert into vcard(username, vcard) "
|
||||
"values ('", Username, "', '", SVCARD, "');"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end).
|
||||
|
||||
export_vcard_search(Server, Output) ->
|
||||
export_common(
|
||||
Server, vcard_search, Output,
|
||||
fun(Host, #vcard_search{user = {User, LServer},
|
||||
luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
middle = Middle, lmiddle = LMiddle,
|
||||
nickname = Nickname, lnickname = LNickname,
|
||||
bday = BDay, lbday = LBDay,
|
||||
ctry = CTRY, lctry = LCTRY,
|
||||
locality = Locality, llocality = LLocality,
|
||||
email = EMail, lemail = LEMail,
|
||||
orgname = OrgName, lorgname = LOrgName,
|
||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
||||
})
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
LUsername = ejabberd_odbc:escape(LUser),
|
||||
|
||||
SFN = ejabberd_odbc:escape(FN),
|
||||
SLFN = ejabberd_odbc:escape(LFN),
|
||||
SFamily = ejabberd_odbc:escape(Family),
|
||||
SLFamily = ejabberd_odbc:escape(LFamily),
|
||||
SGiven = ejabberd_odbc:escape(Given),
|
||||
SLGiven = ejabberd_odbc:escape(LGiven),
|
||||
SMiddle = ejabberd_odbc:escape(Middle),
|
||||
SLMiddle = ejabberd_odbc:escape(LMiddle),
|
||||
SNickname = ejabberd_odbc:escape(Nickname),
|
||||
SLNickname = ejabberd_odbc:escape(LNickname),
|
||||
SBDay = ejabberd_odbc:escape(BDay),
|
||||
SLBDay = ejabberd_odbc:escape(LBDay),
|
||||
SCTRY = ejabberd_odbc:escape(CTRY),
|
||||
SLCTRY = ejabberd_odbc:escape(LCTRY),
|
||||
SLocality = ejabberd_odbc:escape(Locality),
|
||||
SLLocality = ejabberd_odbc:escape(LLocality),
|
||||
SEMail = ejabberd_odbc:escape(EMail),
|
||||
SLEMail = ejabberd_odbc:escape(LEMail),
|
||||
SOrgName = ejabberd_odbc:escape(OrgName),
|
||||
SLOrgName = ejabberd_odbc:escape(LOrgName),
|
||||
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
|
||||
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
|
||||
|
||||
["delete from vcard_search where lusername='", LUsername, "';"
|
||||
"insert into vcard_search("
|
||||
" username, lusername, fn, lfn, family, lfamily,"
|
||||
" given, lgiven, middle, lmiddle, nickname, lnickname,"
|
||||
" bday, lbday, ctry, lctry, locality, llocality,"
|
||||
" email, lemail, orgname, lorgname, orgunit, lorgunit)"
|
||||
"values (",
|
||||
" '", Username, "', '", LUsername, "',"
|
||||
" '", SFN, "', '", SLFN, "',"
|
||||
" '", SFamily, "', '", SLFamily, "',"
|
||||
" '", SGiven, "', '", SLGiven, "',"
|
||||
" '", SMiddle, "', '", SLMiddle, "',"
|
||||
" '", SNickname, "', '", SLNickname, "',"
|
||||
" '", SBDay, "', '", SLBDay, "',"
|
||||
" '", SCTRY, "', '", SLCTRY, "',"
|
||||
" '", SLocality, "', '", SLLocality, "',"
|
||||
" '", SEMail, "', '", SLEMail, "',"
|
||||
" '", SOrgName, "', '", SLOrgName, "',"
|
||||
" '", SOrgUnit, "', '", SLOrgUnit, "');"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
export_common(Server, Table, Output, ConvertFun) ->
|
||||
IO = case Output of
|
||||
odbc ->
|
||||
odbc;
|
||||
_ ->
|
||||
{ok, IODevice} = file:open(Output, [write, raw]),
|
||||
IODevice
|
||||
end,
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:read_lock_table(Table),
|
||||
LServer = jlib:nameprep(Server),
|
||||
{_N, SQLs} =
|
||||
mnesia:foldl(
|
||||
fun(R, {N, SQLs} = Acc) ->
|
||||
case ConvertFun(LServer, R) of
|
||||
[] ->
|
||||
Acc;
|
||||
SQL ->
|
||||
if
|
||||
N < ?MAX_RECORDS_PER_TRANSACTION - 1 ->
|
||||
{N + 1, [SQL | SQLs]};
|
||||
true ->
|
||||
output(LServer, IO,
|
||||
["begin;",
|
||||
lists:reverse([SQL | SQLs]),
|
||||
"commit"]),
|
||||
{0, []}
|
||||
end
|
||||
end
|
||||
end, {0, []}, Table),
|
||||
output(LServer, IO,
|
||||
["begin;",
|
||||
lists:reverse(SQLs),
|
||||
"commit"])
|
||||
end).
|
||||
|
||||
output(LServer, IO, SQL) ->
|
||||
case IO of
|
||||
odbc ->
|
||||
catch ejabberd_odbc:sql_query(LServer, SQL);
|
||||
_ ->
|
||||
file:write(IO, [SQL, $;, $\n])
|
||||
end.
|
||||
|
||||
record_to_string(#roster{usj = {User, Server, JID},
|
||||
name = Name,
|
||||
subscription = Subscription,
|
||||
ask = Ask,
|
||||
askmessage = AskMessage}) ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(JID)),
|
||||
Nick = ejabberd_odbc:escape(Name),
|
||||
SSubscription = case Subscription of
|
||||
both -> "B";
|
||||
to -> "T";
|
||||
from -> "F";
|
||||
none -> "N"
|
||||
end,
|
||||
SAsk = case Ask of
|
||||
subscribe -> "S";
|
||||
unsubscribe -> "U";
|
||||
both -> "B";
|
||||
out -> "O";
|
||||
in -> "I";
|
||||
none -> "N"
|
||||
end,
|
||||
["("
|
||||
"'", Username, "',"
|
||||
"'", SJID, "',"
|
||||
"'", Nick, "',"
|
||||
"'", SSubscription, "',"
|
||||
"'", SAsk, "',"
|
||||
"'", AskMessage, "',"
|
||||
"'N', '', 'item')"].
|
||||
|
||||
groups_to_string(#roster{usj = {User, Server, JID},
|
||||
groups = Groups}) ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(JID)),
|
||||
[["("
|
||||
"'", Username, "',"
|
||||
"'", SJID, "',"
|
||||
"'", ejabberd_odbc:escape(Group), "')"] || Group <- Groups].
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
-- LDAPv3 ASN.1 specification, taken from RFC 2251
|
||||
|
||||
-- Lightweight-Directory-Access-Protocol-V3 DEFINITIONS
|
||||
ELDAPv3 DEFINITIONS
|
||||
IMPLICIT TAGS ::=
|
||||
|
||||
BEGIN
|
||||
|
||||
LDAPMessage ::= SEQUENCE {
|
||||
messageID MessageID,
|
||||
protocolOp CHOICE {
|
||||
bindRequest BindRequest,
|
||||
bindResponse BindResponse,
|
||||
unbindRequest UnbindRequest,
|
||||
searchRequest SearchRequest,
|
||||
searchResEntry SearchResultEntry,
|
||||
searchResDone SearchResultDone,
|
||||
searchResRef SearchResultReference,
|
||||
modifyRequest ModifyRequest,
|
||||
modifyResponse ModifyResponse,
|
||||
addRequest AddRequest,
|
||||
addResponse AddResponse,
|
||||
delRequest DelRequest,
|
||||
delResponse DelResponse,
|
||||
modDNRequest ModifyDNRequest,
|
||||
modDNResponse ModifyDNResponse,
|
||||
compareRequest CompareRequest,
|
||||
compareResponse CompareResponse,
|
||||
abandonRequest AbandonRequest,
|
||||
extendedReq ExtendedRequest,
|
||||
extendedResp ExtendedResponse },
|
||||
controls [0] Controls OPTIONAL }
|
||||
|
||||
MessageID ::= INTEGER (0 .. maxInt)
|
||||
|
||||
maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||
|
||||
LDAPString ::= OCTET STRING
|
||||
|
||||
LDAPOID ::= OCTET STRING
|
||||
|
||||
LDAPDN ::= LDAPString
|
||||
|
||||
RelativeLDAPDN ::= LDAPString
|
||||
|
||||
AttributeType ::= LDAPString
|
||||
|
||||
AttributeDescription ::= LDAPString
|
||||
|
||||
|
||||
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 44]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
AttributeDescriptionList ::= SEQUENCE OF
|
||||
AttributeDescription
|
||||
|
||||
AttributeValue ::= OCTET STRING
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeDesc AttributeDescription,
|
||||
assertionValue AssertionValue }
|
||||
|
||||
AssertionValue ::= OCTET STRING
|
||||
|
||||
Attribute ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
MatchingRuleId ::= LDAPString
|
||||
|
||||
LDAPResult ::= SEQUENCE {
|
||||
resultCode ENUMERATED {
|
||||
success (0),
|
||||
operationsError (1),
|
||||
protocolError (2),
|
||||
timeLimitExceeded (3),
|
||||
sizeLimitExceeded (4),
|
||||
compareFalse (5),
|
||||
compareTrue (6),
|
||||
authMethodNotSupported (7),
|
||||
strongAuthRequired (8),
|
||||
-- 9 reserved --
|
||||
referral (10), -- new
|
||||
adminLimitExceeded (11), -- new
|
||||
unavailableCriticalExtension (12), -- new
|
||||
confidentialityRequired (13), -- new
|
||||
saslBindInProgress (14), -- new
|
||||
noSuchAttribute (16),
|
||||
undefinedAttributeType (17),
|
||||
inappropriateMatching (18),
|
||||
constraintViolation (19),
|
||||
attributeOrValueExists (20),
|
||||
invalidAttributeSyntax (21),
|
||||
-- 22-31 unused --
|
||||
noSuchObject (32),
|
||||
aliasProblem (33),
|
||||
invalidDNSyntax (34),
|
||||
-- 35 reserved for undefined isLeaf --
|
||||
aliasDereferencingProblem (36),
|
||||
-- 37-47 unused --
|
||||
inappropriateAuthentication (48),
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 45]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
invalidCredentials (49),
|
||||
insufficientAccessRights (50),
|
||||
busy (51),
|
||||
unavailable (52),
|
||||
unwillingToPerform (53),
|
||||
loopDetect (54),
|
||||
-- 55-63 unused --
|
||||
namingViolation (64),
|
||||
objectClassViolation (65),
|
||||
notAllowedOnNonLeaf (66),
|
||||
notAllowedOnRDN (67),
|
||||
entryAlreadyExists (68),
|
||||
objectClassModsProhibited (69),
|
||||
-- 70 reserved for CLDAP --
|
||||
affectsMultipleDSAs (71), -- new
|
||||
-- 72-79 unused --
|
||||
other (80) },
|
||||
-- 81-90 reserved for APIs --
|
||||
matchedDN LDAPDN,
|
||||
errorMessage LDAPString,
|
||||
referral [3] Referral OPTIONAL }
|
||||
|
||||
Referral ::= SEQUENCE OF LDAPURL
|
||||
|
||||
LDAPURL ::= LDAPString -- limited to characters permitted in URLs
|
||||
|
||||
Controls ::= SEQUENCE OF Control
|
||||
|
||||
Control ::= SEQUENCE {
|
||||
controlType LDAPOID,
|
||||
criticality BOOLEAN DEFAULT FALSE,
|
||||
controlValue OCTET STRING OPTIONAL }
|
||||
|
||||
BindRequest ::= [APPLICATION 0] SEQUENCE {
|
||||
version INTEGER (1 .. 127),
|
||||
name LDAPDN,
|
||||
authentication AuthenticationChoice }
|
||||
|
||||
AuthenticationChoice ::= CHOICE {
|
||||
simple [0] OCTET STRING,
|
||||
-- 1 and 2 reserved
|
||||
sasl [3] SaslCredentials }
|
||||
|
||||
SaslCredentials ::= SEQUENCE {
|
||||
mechanism LDAPString,
|
||||
credentials OCTET STRING OPTIONAL }
|
||||
|
||||
BindResponse ::= [APPLICATION 1] SEQUENCE {
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 46]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
COMPONENTS OF LDAPResult,
|
||||
serverSaslCreds [7] OCTET STRING OPTIONAL }
|
||||
|
||||
UnbindRequest ::= [APPLICATION 2] NULL
|
||||
|
||||
SearchRequest ::= [APPLICATION 3] SEQUENCE {
|
||||
baseObject LDAPDN,
|
||||
scope ENUMERATED {
|
||||
baseObject (0),
|
||||
singleLevel (1),
|
||||
wholeSubtree (2) },
|
||||
derefAliases ENUMERATED {
|
||||
neverDerefAliases (0),
|
||||
derefInSearching (1),
|
||||
derefFindingBaseObj (2),
|
||||
derefAlways (3) },
|
||||
sizeLimit INTEGER (0 .. maxInt),
|
||||
timeLimit INTEGER (0 .. maxInt),
|
||||
typesOnly BOOLEAN,
|
||||
filter Filter,
|
||||
attributes AttributeDescriptionList }
|
||||
|
||||
Filter ::= CHOICE {
|
||||
and [0] SET OF Filter,
|
||||
or [1] SET OF Filter,
|
||||
not [2] Filter,
|
||||
equalityMatch [3] AttributeValueAssertion,
|
||||
substrings [4] SubstringFilter,
|
||||
greaterOrEqual [5] AttributeValueAssertion,
|
||||
lessOrEqual [6] AttributeValueAssertion,
|
||||
present [7] AttributeDescription,
|
||||
approxMatch [8] AttributeValueAssertion,
|
||||
extensibleMatch [9] MatchingRuleAssertion }
|
||||
|
||||
SubstringFilter ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
-- at least one must be present
|
||||
substrings SEQUENCE OF CHOICE {
|
||||
initial [0] LDAPString,
|
||||
any [1] LDAPString,
|
||||
final [2] LDAPString } }
|
||||
|
||||
MatchingRuleAssertion ::= SEQUENCE {
|
||||
matchingRule [1] MatchingRuleId OPTIONAL,
|
||||
type [2] AttributeDescription OPTIONAL,
|
||||
matchValue [3] AssertionValue,
|
||||
dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 47]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||
objectName LDAPDN,
|
||||
attributes PartialAttributeList }
|
||||
|
||||
PartialAttributeList ::= SEQUENCE OF SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL
|
||||
|
||||
SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||
|
||||
ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||
object LDAPDN,
|
||||
modification SEQUENCE OF SEQUENCE {
|
||||
operation ENUMERATED {
|
||||
add (0),
|
||||
delete (1),
|
||||
replace (2) },
|
||||
modification AttributeTypeAndValues } }
|
||||
|
||||
AttributeTypeAndValues ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||
|
||||
AddRequest ::= [APPLICATION 8] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
attributes AttributeList }
|
||||
|
||||
AttributeList ::= SEQUENCE OF SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
AddResponse ::= [APPLICATION 9] LDAPResult
|
||||
|
||||
DelRequest ::= [APPLICATION 10] LDAPDN
|
||||
|
||||
DelResponse ::= [APPLICATION 11] LDAPResult
|
||||
|
||||
ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
newrdn RelativeLDAPDN,
|
||||
deleteoldrdn BOOLEAN,
|
||||
newSuperior [0] LDAPDN OPTIONAL }
|
||||
|
||||
ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 48]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
CompareRequest ::= [APPLICATION 14] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
ava AttributeValueAssertion }
|
||||
|
||||
CompareResponse ::= [APPLICATION 15] LDAPResult
|
||||
|
||||
AbandonRequest ::= [APPLICATION 16] MessageID
|
||||
|
||||
ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
|
||||
requestName [0] LDAPOID,
|
||||
requestValue [1] OCTET STRING OPTIONAL }
|
||||
|
||||
ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||
COMPONENTS OF LDAPResult,
|
||||
responseName [10] LDAPOID OPTIONAL,
|
||||
response [11] OCTET STRING OPTIONAL }
|
||||
|
||||
END
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user