Compare commits
17 Commits
Author | SHA1 | Date |
---|---|---|
badlop | a3c7a5d086 | |
Mickaël Rémond | b6a4ba0e6c | |
Oleg Salionov | 374eabfee3 | |
Oleg Salionov | 50f3a42de8 | |
Tim Stewart | 0df57edeef | |
Tim Stewart | d265730f31 | |
Tim Stewart | d744d77288 | |
Ben Langfeld | c28cb3b4c2 | |
Ben Langfeld | d974cb618d | |
Ben Langfeld | 04df0724f3 | |
Ben Langfeld | a0e27ffe65 | |
Badlop | 93bba1d1fd | |
Badlop | 93e0695c88 | |
Badlop | c84d51a992 | |
Badlop | a03088d033 | |
Badlop | 360ed6a2e6 | |
Badlop | 2e5bc8d269 |
|
@ -1,162 +0,0 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.spec'
|
||||
- '**.txt'
|
||||
- '*/conf/*.yml'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.spec'
|
||||
- '**.txt'
|
||||
- '*/conf/*.yml'
|
||||
|
||||
jobs:
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
otp: ['21.3', '25.3']
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: erlang:${{ matrix.otp }}
|
||||
|
||||
steps:
|
||||
|
||||
- name: Checkout ejabberd
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: processone/ejabberd
|
||||
|
||||
- name: Checkout ejabberd-contrib
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: .ejabberd-modules/sources/ejabberd-contrib
|
||||
|
||||
- name: Get a compatible Rebar3
|
||||
if: matrix.otp <= '21.3'
|
||||
run: |
|
||||
rm rebar3
|
||||
wget https://github.com/processone/ejabberd/raw/21.12/rebar3
|
||||
chmod +x rebar3
|
||||
|
||||
- name: Prepare libraries
|
||||
run: |
|
||||
apt-get -qq update
|
||||
apt-get -y purge libgd3 nginx
|
||||
apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Prepare rebar
|
||||
id: rebar
|
||||
run: |
|
||||
echo '{xref_ignores, [{eldap_filter_yecc, return_error, 2},
|
||||
{fusco_lib, split_credentials, 1},
|
||||
{http_uri, encode, 1},
|
||||
{http_uri, decode, 1}
|
||||
]}.' >>rebar.config
|
||||
echo '{xref_checks, [deprecated_function_calls, deprecated_functions,
|
||||
locals_not_used, undefined_function_calls, undefined_functions]}.
|
||||
% Disabled: exports_not_used,' >>rebar.config
|
||||
echo '{dialyzer, [{get_warnings, true}, {plt_extra_apps, [cache_tab,
|
||||
eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml,
|
||||
mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix,
|
||||
sqlite3, stringprep, stun, xmpp, yconf]} ]}.' >>rebar.config
|
||||
echo '{ct_extra_params, "-verbosity 20"}.' >>rebar.config
|
||||
|
||||
- name: Remove syntax_tools from release
|
||||
run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
|
||||
|
||||
- name: Cache rebar
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/rebar3/
|
||||
key: ${{matrix.otp}}-${{hashFiles('rebar.config')}}
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --with-rebar=./rebar3 \
|
||||
--prefix=/tmp/ejabberd \
|
||||
--enable-all \
|
||||
--disable-elixir \
|
||||
--disable-mssql \
|
||||
--disable-odbc
|
||||
make update
|
||||
make
|
||||
|
||||
- name: Start ejabberd
|
||||
run: |
|
||||
echo "CONTRIB_MODULES_PATH=`pwd`/.ejabberd-modules" >> ejabberdctl.cfg.example
|
||||
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||
make dev
|
||||
$CTL start
|
||||
$CTL started
|
||||
|
||||
- name: Enable mod_muc_log
|
||||
run: |
|
||||
echo ' mod_muc_log: {}' >>.ejabberd-modules/sources/ejabberd-contrib/mod_muc_log_http/conf/mod_muc_log_http.yml
|
||||
|
||||
- name: Get list of available modules
|
||||
run: |
|
||||
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||
$CTL modules_available | awk '{print $1}' | grep -v mod_captcha_rust >modules_available.txt
|
||||
|
||||
- name: Install modules
|
||||
run: |
|
||||
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||
for i in `cat modules_available.txt` ; do
|
||||
echo "Installing $i"
|
||||
$CTL module_install $i
|
||||
done
|
||||
|
||||
- name: Copy modules
|
||||
run: |
|
||||
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||
for i in `cat modules_available.txt` ; do
|
||||
echo "Copying from $i"
|
||||
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/include/*.hrl" -exec 'cp' '{}' 'include/' ';'
|
||||
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/src/*.erl" -exec 'cp' '{}' 'src/' ';'
|
||||
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/deps/*/ebin/*.beam" -exec 'cp' '{}' '_build/default/lib/ejabberd/ebin/' ';'
|
||||
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/deps/*/include/*.hrl" -exec 'cp' '{}' 'include/' ';'
|
||||
done
|
||||
|
||||
- name: Uninstall modules
|
||||
run: |
|
||||
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||
for i in `cat modules_available.txt` ; do
|
||||
echo "Uninstalling $i"
|
||||
$CTL module_uninstall $i
|
||||
done
|
||||
|
||||
# This doesn't work right now, because epmd is in another path
|
||||
# - run: ./ejabberdctl stop && ./ejabberdctl stopped
|
||||
|
||||
- run: make
|
||||
- run: make hooks
|
||||
- run: make options
|
||||
- run: make xref
|
||||
|
||||
- name: Run Dialyzer
|
||||
if: always()
|
||||
run: |
|
||||
rm -rf _build/default/lib/ejabberd/ebin/fusco*
|
||||
rm -rf _build/default/lib/ejabberd/ebin/observer_cli*
|
||||
make dialyzer # Too many errors... first fix them, then enable this
|
||||
|
||||
- name: View logs dir
|
||||
if: always()
|
||||
run: ls -la _build/dev/rel/ejabberd/logs
|
||||
- name: View ejabberd.log
|
||||
if: always()
|
||||
run: cat _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
- name: View error.log
|
||||
if: always()
|
||||
run: cat _build/dev/rel/ejabberd/logs/error.log
|
||||
|
|
@ -1 +0,0 @@
|
|||
*.beam
|
85
README.md
85
README.md
|
@ -1,85 +0,0 @@
|
|||
ejabberd-contrib
|
||||
================
|
||||
|
||||
This is a collaborative development area for ejabberd module developers
|
||||
and users.
|
||||
|
||||
Those modules are not officially supported by ProcessOne.
|
||||
|
||||
For users
|
||||
---------
|
||||
|
||||
To use an ejabberd module coming from this repository:
|
||||
|
||||
- You need to have ejabberd installed.
|
||||
|
||||
- If you have not already done it, run `ejabberdctl modules_update_specs`
|
||||
to retrieve the list of available modules.
|
||||
|
||||
- Run `ejabberdctl module_install <module>` to get the source code and to
|
||||
compile and install the `beam` file into ejabberd's module search path.
|
||||
This path is either `~/.ejabberd-modules` or defined by the
|
||||
`CONTRIB_MODULES_PATH` setting in `ejabberdctl.cfg`.
|
||||
|
||||
- Edit the configuration file provided in the `conf` directory of the
|
||||
installed module and update it to your needs. Or, if you prefer so,
|
||||
configure it in your main ejabberd configuration file.
|
||||
|
||||
- Run `ejabberdctl module_uninstall <module>` to remove a module from
|
||||
ejabberd.
|
||||
|
||||
|
||||
For developers
|
||||
--------------
|
||||
|
||||
The following organization has been set up for the development:
|
||||
|
||||
- Development and compilation of modules is done by ejabberd. You need
|
||||
ejabberd installed. Use `ejabberdctl module_check <module>` to ensure it
|
||||
compiles correctly before committing your work. The sources of your
|
||||
module must be located in `$CONTRIB_MODULES_PATH/sources/<module>`.
|
||||
|
||||
- Compilation can by done manually (if you know what you are doing) so you
|
||||
don't need ejabberd running:
|
||||
```
|
||||
cd /path/of/module
|
||||
mkdir ebin
|
||||
/path/of/ejabberd's/erlc \
|
||||
-o ebin \
|
||||
-I include -I /path/of/ejabberd/lib/ejabberd-XX.YY/include \
|
||||
-DLAGER -DNO_EXT_LIB \
|
||||
src/*erl
|
||||
```
|
||||
|
||||
- The module directory structure is usually the following:
|
||||
* `README.txt`: Module description.
|
||||
* `COPYING`: License for the module.
|
||||
* `doc/`: Documentation directory.
|
||||
* `src/`: Erlang source directory.
|
||||
* `lib/`: Elixir source directory.
|
||||
* `priv/msgs/`: Directory with translation files (pot, po and msg).
|
||||
* `conf/<module>.yml`: Configuration for your module.
|
||||
* `<module>.spec`: Yaml description file for your module.
|
||||
|
||||
- Module developers should note in the `README.txt` file whether the
|
||||
module has requirements or known incompatibilities with other modules.
|
||||
|
||||
- If your module project contains several erlang modules, you should export a
|
||||
function pre_uninstall/0 in the main one listing the other ones.
|
||||
See mod_statsdx as an example.
|
||||
|
||||
|
||||
Broken modules
|
||||
--------------
|
||||
|
||||
This is the list of modules that are known to be broken with latest ejabberd master branch.
|
||||
|
||||
If you feel they are worth it, your help to fix them is welcome:
|
||||
|
||||
- atom_pubsub: "Provides access to all PEP nodes via an AtomPub interface."
|
||||
- ircd: "This is an IRC server frontend to ejabberd."
|
||||
- mod_archive: "Message Archiving (XEP-0136)."
|
||||
- mod_irc: "IRC transport."
|
||||
- mod_mam_mnesia: This feature got included in ejabberd 15.06
|
||||
- mod_openid: "Transform the Jabber Server in an openid provider."
|
||||
- mod_profile: "User Profile (XEP-0154) in Mnesia table."
|
|
@ -0,0 +1,60 @@
|
|||
ejabberd-modules is a collaborative development area for ejabberd
|
||||
modules developers and users.
|
||||
|
||||
|
||||
For users
|
||||
=========
|
||||
|
||||
You need to have Erlang installed.
|
||||
|
||||
To use an ejabberd module coming from this repository:
|
||||
|
||||
- Read the module specific README to see if special steps are needed
|
||||
to deploy it.
|
||||
|
||||
- Run "./build.sh" or "build.bat" in the root (usually trunk
|
||||
directory) of the wanted module.
|
||||
|
||||
- Copy generated .beam files from the ebin directory to the directory
|
||||
where your ejabberd .beam files are.
|
||||
|
||||
- Use the configuration file examples provided in the conf dir to
|
||||
update your ejabberd.cfg configuration file.
|
||||
|
||||
If during compilation of a module you get an error like:
|
||||
{"init terminating in do_boot",{undef,[{make,all,[]},...
|
||||
it means Erlang couldn't find its file make.beam
|
||||
In Debian and other distributions you can try to install packages like:
|
||||
erlang-dev erlang-nox erlang-tools
|
||||
|
||||
|
||||
For developers
|
||||
==============
|
||||
|
||||
The following organisation has been set-up for the development:
|
||||
|
||||
- Each module has its own SVN structure (trunk/branches/tags) to allow
|
||||
independent versioning.
|
||||
|
||||
- Development and compilation of module should be possible without
|
||||
ejabberd SVN, as long as developers check-out the ejabberd-dev
|
||||
module. This module contains include file to make compilation
|
||||
possible.
|
||||
|
||||
- The module directory structure is usually the following:
|
||||
README.txt: Module description
|
||||
LICENSE.txt: License for the module
|
||||
Emakefile: Erlang makefile to build the module (preferred way, if no
|
||||
dependencies on C code, as build will thus works on Windows)
|
||||
doc/: Documentation dir
|
||||
src/: Source directory
|
||||
src/msgs/: Directory with translation files (pot, po and msg).
|
||||
ebin/: empty (Target directory for the build).
|
||||
conf/: Directory containing example configuration for your module.
|
||||
build.sh: *nix build script.
|
||||
build.bat: Windows build script.
|
||||
|
||||
- Module developers should put in the README if the module has
|
||||
requirements or known incompatibilities with other modules (for
|
||||
example, by modifying the same main ejabberd modules).
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{'../ejabberd-dev/src/gen_mod', [{outdir, "../ejabberd-dev/ebin"},{i,"../ejabberd-dev/include"}]}.
|
||||
{'src/atom_microblog', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
||||
{'src/atom_pubsub', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
||||
{'src/mod_couch', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
@ -1,20 +1,9 @@
|
|||
|
||||
|
||||
***************
|
||||
PLEASE NOTE
|
||||
***************
|
||||
|
||||
This module does NOT work
|
||||
with ejabberd 13 or newer.
|
||||
|
||||
***************
|
||||
|
||||
|
||||
atom_pubsub - the Atom PubSub tunnel
|
||||
|
||||
Author: Eric Cestari <eric@ohmforce.com> http://www.cestari.info/
|
||||
Licensed under the same terms as ejabberd (GPL 2)
|
||||
Requires: ejabberd 2.0.3 or newer.
|
||||
Requires: ejabberd trunk SVN r1561 or newer; or ejabberd 2.0.3 or newer
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
|
@ -33,6 +22,28 @@ http://www.cestari.info/2008/6/19/atom-pubsub-module-for-ejabberd
|
|||
http://www.cestari.info/2008/9/12/atom_pubsub-dead-long-live-atom_microblog
|
||||
|
||||
|
||||
INSTALL
|
||||
-------
|
||||
|
||||
1. Compile the files executing: ./build.sh
|
||||
|
||||
2. Copy beam files from ebin/ into your ejabberd installation.
|
||||
|
||||
3. Edit ejabberd.cfg and add a request handler in ejabberd_http listener:
|
||||
{listen, [
|
||||
...
|
||||
{8080, ejabberd_http, [
|
||||
http_poll,
|
||||
web_admin,
|
||||
{request_handlers, [{["pep"], atom_microblog}, % Make sure it's "pep", or change it in atom_microblog.erl
|
||||
{["pubsub"], atom_pubsub}]} % Make sure it's "pubsub", or change it in atom_pubsub.erl
|
||||
|
||||
]}
|
||||
]}.
|
||||
|
||||
4. Restart ejabberd.
|
||||
|
||||
|
||||
USAGE
|
||||
-----
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
author: "Eric Cestari <eric at ohmforce.com>"
|
||||
category: "pubsub"
|
||||
summary: "Provides access to all PEP nodes via an AtomPub interface"
|
||||
home: "https://github.com/processone/ejabberd-contrib/tree/master/atom_pubsub"
|
||||
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -0,0 +1 @@
|
|||
erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
erl -pa ../ejabberd-dev/ebin -pz ebin -make
|
|
@ -1,7 +0,0 @@
|
|||
listen:
|
||||
-
|
||||
port: 8080
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"pep": atom_microblog
|
||||
"pubsub": atom_pubsub
|
|
@ -7,9 +7,8 @@
|
|||
-author('eric@ohmforce.com').
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("pubsub.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("mod_pubsub/pubsub.hrl").
|
||||
-include("web/ejabberd_http.hrl").
|
||||
-export([process/2]).
|
||||
|
||||
process([Domain,User|_]=LocalPath, #request{auth = Auth} = Request)->
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
-author('eric@ohmforce.com').
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("pubsub.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("mod_pubsub/pubsub.hrl").
|
||||
-include("web/ejabberd_http.hrl").
|
||||
-export([process/2]).
|
||||
|
||||
process([Domain,User|_]=LocalPath, #request{auth = Auth} = Request)->
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
Copyright (c) 2006, Claes Wikstrom, klacke@hyber.org
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of "bfile" nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
|
||||
all:
|
||||
(cd config;$(MAKE))
|
||||
(cd src;$(MAKE))
|
||||
-(cd c_src;$(MAKE) -k)
|
||||
$(MAKE) appfile
|
||||
|
||||
clean:
|
||||
(cd src;$(MAKE) clean)
|
||||
(cd c_src;$(MAKE) clean)
|
||||
(cd config; $(MAKE) clean)
|
||||
|
||||
|
||||
install: all
|
||||
(cd c_src; $(MAKE) install)
|
||||
|
||||
conf_clean:
|
||||
(cd config; $(MAKE) clean)
|
||||
|
||||
appfile:
|
||||
(cd src;$(MAKE) ../ebin/bfile.app)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
An interface to fast FILE I/O
|
||||
|
||||
It's based on an old and hacked version
|
||||
of the BSD FILE*
|
||||
|
||||
To install, type make; make install
|
||||
and it shuld install itself as an app in your
|
||||
erlang dir.
|
||||
|
||||
See the source src/bfile.erl for API
|
||||
|
||||
Here's an example shell session:
|
||||
|
||||
|
||||
2> bfile:load_driver().
|
||||
ok
|
||||
4> {ok, Fd} = bfile:fopen("Makefile", "r").
|
||||
{ok,{bfile,#Port<0.98>}}
|
||||
5> bfile:fgets(Fd).
|
||||
{line,<<10>>}
|
||||
6> bfile:fgets(Fd).
|
||||
{line,<<10>>}
|
||||
7> bfile:fgets(Fd).
|
||||
{line,<<97,108,108,58,32,10>>}
|
||||
14> bfile:fread(Fd, 10000).
|
||||
{ok,<<10,10,105,110,115,116,97,108,108,58,32,97,108,108,10,9,40,99,100,32,99,95,115,114,99,59,32,...>>}
|
||||
15> bfile:fread(Fd, 10000).
|
||||
eof
|
|
@ -0,0 +1,490 @@
|
|||
/* Interface to stdio buffered FILE io */
|
||||
/* author: klacke@kaja.klacke.net */
|
||||
/* Created : 22 Nov 1999 by Claes Wikstrom <klacke@kaja.klacke.net> */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define USE_STDIO
|
||||
|
||||
#ifdef WIN32
|
||||
#define USE_STDIO
|
||||
#include <windows.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
#include "erl_driver.h"
|
||||
#ifndef ERL_DRV_NIL
|
||||
#include "erl_driver_compat.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_STDIO
|
||||
# include <stdio.h>
|
||||
#else
|
||||
|
||||
# define malloc(s) driver_alloc(s)
|
||||
# define realloc(p, s) driver_realloc(p, s)
|
||||
# define free(p) driver_free(p)
|
||||
|
||||
# define BINTERFACE static
|
||||
# include "bbio.c"
|
||||
# define FILE bFILE
|
||||
# define clearerr bclearerr
|
||||
# define fclose bfclose
|
||||
# define feof bfeof
|
||||
# define ferror bferror
|
||||
# define fflush bfflush
|
||||
# define fgets bfgets
|
||||
# define fileno bfileno
|
||||
# define fopen bfopen
|
||||
# define fread bfread
|
||||
# define fseek bfseek
|
||||
# define ftell bftell
|
||||
# define fwrite bfwrite
|
||||
# define getc bgetc
|
||||
# define ungetc bungetc
|
||||
#endif
|
||||
|
||||
|
||||
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
|
||||
(((unsigned char*) (s))[1] << 16) | \
|
||||
(((unsigned char*) (s))[2] << 8) | \
|
||||
(((unsigned char*) (s))[3]))
|
||||
|
||||
#define put_int32(i, s) {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
|
||||
((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
|
||||
((char*)(s))[2] = (char)((i) >> 8) & 0xff; \
|
||||
((char*)(s))[3] = (char)((i) & 0xff);}
|
||||
/* op codes */
|
||||
|
||||
#define XX_OPEN 'o'
|
||||
#define XX_CLOSE 'c'
|
||||
#define XX_READ 'r'
|
||||
#define XX_WRITE 'w'
|
||||
#define XX_SEEK 's'
|
||||
#define XX_TELL 't'
|
||||
#define XX_TRUNCATE 'T'
|
||||
#define XX_FLUSH 'f'
|
||||
#define XX_OEOF 'e'
|
||||
#define XX_ERROR 'E'
|
||||
#define XX_GETC 'g'
|
||||
#define XX_GETS 'G'
|
||||
#define XX_GETS2 '2'
|
||||
#define XX_SET_LINEBUF_SIZE 'S'
|
||||
#define XX_UNGETC 'u'
|
||||
|
||||
|
||||
|
||||
/* return codes */
|
||||
#define XX_VALUE 'v'
|
||||
#define XX_FLINE 'L'
|
||||
#define XX_OK 'o'
|
||||
#define XX_I32 'O'
|
||||
#define XX_NOLINE 'N'
|
||||
#define XX_FERROR 'E'
|
||||
#define XX_REOF 'x'
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#define XX_EINVAL WSAEINVAL
|
||||
|
||||
|
||||
#else
|
||||
#define XX_EINVAL EINVAL
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ErlDrvData FILE_start(ErlDrvPort port, char *buf);
|
||||
static void FILE_stop(ErlDrvData drv_data);
|
||||
|
||||
static ErlDrvEntry FILE_driver_entry;
|
||||
|
||||
typedef struct _desc {
|
||||
ErlDrvPort port;
|
||||
FILE *fp;
|
||||
int linebuf_size;
|
||||
} Desc;
|
||||
|
||||
|
||||
static ErlDrvData FILE_start(ErlDrvPort port, char *buf)
|
||||
{
|
||||
Desc *d = (Desc*) driver_alloc(sizeof (Desc));
|
||||
|
||||
if (d == NULL)
|
||||
return (ErlDrvData) -1;
|
||||
d->fp = NULL;
|
||||
d->port = port;
|
||||
d->linebuf_size = 255; /* default line size */
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
|
||||
return (ErlDrvData) d;
|
||||
}
|
||||
|
||||
|
||||
static void FILE_stop(ErlDrvData drv_data)
|
||||
{
|
||||
Desc *d = (Desc*) drv_data;
|
||||
if (d->fp)
|
||||
fclose(d->fp);
|
||||
driver_free(d);
|
||||
}
|
||||
|
||||
|
||||
static char *driver_error(ErlDrvPort port, int err)
|
||||
{
|
||||
char response[256]; /* Response buffer. */
|
||||
char* s;
|
||||
char* t;
|
||||
ErlDrvBinary* bin;
|
||||
|
||||
bin = driver_alloc_binary(1);
|
||||
bin->orig_bytes[0] = XX_FERROR;
|
||||
|
||||
response[0] = XX_FERROR;
|
||||
for (s = erl_errno_id(err), t = bin->orig_bytes + 1; *s; s++, t++)
|
||||
*t = tolower(*s);
|
||||
return (char *)bin;
|
||||
}
|
||||
|
||||
static char *driver_ret32(ErlDrvPort port, unsigned int r)
|
||||
{
|
||||
char ch = XX_I32;
|
||||
ErlDrvBinary* bin;
|
||||
|
||||
bin = driver_alloc_binary(1);
|
||||
bin->orig_bytes[0] = ch;
|
||||
put_int32(r, bin->orig_bytes + 1);
|
||||
return (char *)bin;
|
||||
}
|
||||
|
||||
static char *driver_ok(ErlDrvPort port)
|
||||
{
|
||||
char ch = XX_OK;
|
||||
ErlDrvBinary* bin;
|
||||
bin = driver_alloc_binary(1);
|
||||
bin->orig_bytes[0] = ch;
|
||||
return (char *)bin;
|
||||
}
|
||||
|
||||
static char *driver_eof(ErlDrvPort port)
|
||||
{
|
||||
char ch = XX_REOF;
|
||||
ErlDrvBinary* bin;
|
||||
bin = driver_alloc_binary(1);
|
||||
bin->orig_bytes[0] = ch;
|
||||
return (char *)bin;
|
||||
}
|
||||
|
||||
|
||||
static int FILE_control(ErlDrvData drv_data,
|
||||
unsigned int command,
|
||||
char *buf, int len,
|
||||
char **rbuf, int rlen)
|
||||
{
|
||||
Desc *desc = (Desc*) drv_data;
|
||||
ErlDrvBinary* bin;
|
||||
|
||||
switch (command) {
|
||||
|
||||
case XX_OPEN: {
|
||||
char file[BUFSIZ]; /* should be FILENAME_MAX */
|
||||
char flags[4]; /* at most someething like rb+ */
|
||||
char* src;
|
||||
char* dst;
|
||||
char* src_end;
|
||||
char* dst_end;
|
||||
|
||||
if (desc->fp != NULL) {
|
||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* play it safe ? */
|
||||
src = buf;
|
||||
src_end = buf + len;
|
||||
|
||||
/* get file name */
|
||||
dst = file;
|
||||
dst_end = dst + BUFSIZ; /* make room for a '\0' */
|
||||
while((src < src_end) && (dst < dst_end) && (*src != '\0'))
|
||||
*dst++ = *src++;
|
||||
if ((src == src_end) || (dst == dst_end)) {
|
||||
driver_error(desc->port, XX_EINVAL);
|
||||
}
|
||||
*dst = *src++;
|
||||
/* get flags */
|
||||
dst = flags;
|
||||
dst_end = dst + 4;
|
||||
while((src < src_end) && (dst < dst_end) && (*src != '\0'))
|
||||
*dst++ = *src++;
|
||||
if (dst == dst_end) {
|
||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
||||
return 1;
|
||||
}
|
||||
*dst = '\0';
|
||||
|
||||
if (src + 1 != src_end) {
|
||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((desc->fp = fopen(file, flags))==NULL) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case XX_WRITE: {
|
||||
if (fwrite(buf, 1, len, desc->fp) != len) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case XX_READ: {
|
||||
char ch = XX_VALUE;
|
||||
int rval;
|
||||
int sz = get_int32(buf);
|
||||
|
||||
if ((bin = driver_alloc_binary(sz + 1)) == NULL) {
|
||||
*rbuf = driver_error(desc->port, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bin->orig_bytes[0] = ch;
|
||||
if ((rval = fread(bin->orig_bytes + 1, 1, sz, desc->fp)) != sz) {
|
||||
if (feof(desc->fp)) {
|
||||
if (rval == 0) {
|
||||
driver_free_binary(bin);
|
||||
*rbuf = driver_eof(desc->port);
|
||||
return 1;
|
||||
}
|
||||
bin = driver_realloc_binary(bin, rval + 1);
|
||||
*rbuf = (char *)bin;
|
||||
return 1;
|
||||
}
|
||||
driver_free_binary(bin);
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = (char *)bin;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case XX_SEEK: {
|
||||
int offs = get_int32(buf);
|
||||
int w = (int) buf[4];
|
||||
int whence;
|
||||
switch (w) {
|
||||
case 1: whence = SEEK_SET; break;
|
||||
case 2: whence = SEEK_CUR; break;
|
||||
case 3: whence = SEEK_END; break;
|
||||
}
|
||||
if ((w = fseek(desc->fp, offs, whence)) != 0) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case XX_TELL: {
|
||||
int offs;
|
||||
if ((offs = ftell(desc->fp)) == -1) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_ret32(desc->port, offs);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case XX_TRUNCATE: {
|
||||
int fno;
|
||||
int offs;
|
||||
/* is this really safe? */
|
||||
if (fflush(desc->fp) != 0) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
if ((offs = ftell(desc->fp)) == -1) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
fno = fileno(desc->fp);
|
||||
#ifdef WIN32
|
||||
if (SetEndOfFile((HANDLE)fno) != 0) {
|
||||
*rbuf = driver_error(desc->port, GetLastError());
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
if (ftruncate(fno, offs) == -1) {
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case XX_FLUSH:
|
||||
if (fflush(desc->fp) != 0)
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
else
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case XX_OEOF:
|
||||
if (feof(desc->fp))
|
||||
*rbuf = driver_ret32(desc->port, 1);
|
||||
else
|
||||
*rbuf = driver_ret32(desc->port, 0);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case XX_ERROR:
|
||||
if (ferror(desc->fp))
|
||||
*rbuf = driver_ret32(desc->port, 1);
|
||||
else
|
||||
*rbuf = driver_ret32(desc->port,0);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case XX_GETC: {
|
||||
int ch;
|
||||
if ((ch = getc(desc->fp)) == EOF) {
|
||||
if (feof(desc->fp)) {
|
||||
*rbuf = driver_eof(desc->port);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_ret32(desc->port, ch);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case XX_SET_LINEBUF_SIZE: {
|
||||
int sz = get_int32(buf);
|
||||
desc->linebuf_size = sz;
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case XX_GETS:
|
||||
case XX_GETS2: {
|
||||
int rval;
|
||||
long cpos1, cpos2;
|
||||
char header;
|
||||
|
||||
if ((bin = driver_alloc_binary(desc->linebuf_size + 1)) == NULL) {
|
||||
*rbuf = driver_error(desc->port, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((cpos1 = ftell(desc->fp)) == -1) {
|
||||
driver_free_binary(bin);
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((fgets(bin->orig_bytes + 1, desc->linebuf_size,
|
||||
desc->fp)) == NULL) {
|
||||
driver_free_binary(bin);
|
||||
if (feof(desc->fp)) {
|
||||
*rbuf = driver_eof(desc->port);
|
||||
return 1;
|
||||
}
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
if ((cpos2 = ftell(desc->fp)) == -1) {
|
||||
driver_free_binary(bin);
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
return 1;
|
||||
}
|
||||
rval = cpos2 - cpos1;
|
||||
|
||||
if (bin->orig_bytes[rval] == '\n' &&
|
||||
bin->orig_bytes[rval + 1] == 0) {
|
||||
header = XX_FLINE;
|
||||
/* GETS keep newline, GETS2 remove newline */
|
||||
rval = rval - (command == XX_GETS ? 0 : 1);
|
||||
}
|
||||
else
|
||||
header = XX_NOLINE;
|
||||
bin->orig_bytes[0] = header;
|
||||
bin = driver_realloc_binary(bin, rval + 1);
|
||||
*rbuf = (char *)bin;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case XX_UNGETC: {
|
||||
int ch = buf[0];
|
||||
if (ungetc(ch, desc->fp) == EOF)
|
||||
*rbuf = driver_error(desc->port, errno);
|
||||
else
|
||||
*rbuf = driver_ok(desc->port);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Unknown opcode %c\n\r", command);
|
||||
#endif
|
||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void FILE_finish()
|
||||
{
|
||||
#ifndef USE_STDIO
|
||||
/*
|
||||
* Make sure any remaining buffers are flushed (this is done on exit() by
|
||||
* the normal stdio).
|
||||
*/
|
||||
bbio_cleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize and return a driver entry struct
|
||||
*/
|
||||
|
||||
DRIVER_INIT(FILE_drv)
|
||||
{
|
||||
FILE_driver_entry.init = NULL; /* Not used */
|
||||
FILE_driver_entry.start = FILE_start;
|
||||
FILE_driver_entry.stop = FILE_stop;
|
||||
FILE_driver_entry.output = NULL;
|
||||
FILE_driver_entry.ready_input = NULL;
|
||||
FILE_driver_entry.ready_output = NULL;
|
||||
FILE_driver_entry.driver_name = "FILE_drv";
|
||||
FILE_driver_entry.finish = FILE_finish;
|
||||
FILE_driver_entry.outputv = NULL;
|
||||
FILE_driver_entry.control = FILE_control;
|
||||
return &FILE_driver_entry;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
include ../config/include.mk
|
||||
|
||||
## don't build this under win32 at all
|
||||
ifdef WIN32
|
||||
|
||||
PRIV_FILES =
|
||||
|
||||
else
|
||||
|
||||
PRIV_FILES=../priv/FILE_drv.so
|
||||
|
||||
endif
|
||||
|
||||
|
||||
CFLAGS += -I$(ERL_C_INCLUDE_DIR) -I../config -I.
|
||||
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
|
||||
all: $(PRIV_FILES)
|
||||
|
||||
clean:
|
||||
-rm -f $(PRIV_FILES) FILE_drv.o
|
||||
|
||||
|
||||
install:
|
||||
install -d $(ERLDIR)/lib/bfile
|
||||
cp -r `pwd` $(ERLDIR)/lib/bfile
|
||||
cp -r `pwd`/../ebin $(ERLDIR)/lib/bfile
|
||||
cp -r `pwd`/../priv $(ERLDIR)/lib/bfile
|
||||
|
||||
|
||||
../priv/FILE_drv.so: FILE_drv.o
|
||||
$(LD_SHARED) -o $@ FILE_drv.o $(LIBS)
|
||||
|
||||
|
||||
FILE_drv.o: FILE_drv.c
|
||||
$(CC) -o $@ -c -fpic $(CFLAGS) -DDYNAMIC_DRIVER FILE_drv.c
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MK_INCLUDE=include.mk
|
||||
CONFIG_H=config.h
|
||||
|
||||
all: config.status $(MK_INCLUDE) $(CONFIG_H)
|
||||
|
||||
config.status: configure
|
||||
./configure
|
||||
|
||||
$(CONFIG_H) $(MK_INCLUDE): config.status include.mk.in config.h.in
|
||||
./config.status
|
||||
|
||||
configure: configure.in
|
||||
autoheader
|
||||
autoconf
|
||||
|
||||
clean:
|
||||
-rm -f config.cache config.log config.status configure \
|
||||
$(MK_INCLUDE) $(CONFIG_H)
|
||||
-rm -rf autom4te.cache
|
||||
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
@TOP@
|
||||
|
||||
/*
|
||||
* This file contains prototypes for config.h.in which autoheader
|
||||
* could not figure by itself. I.e. if you write your own test which
|
||||
* defines a macro you will probably have to put it here, but if you
|
||||
* use any standard test AC_* it will be detected by autoheader.
|
||||
*/
|
||||
|
||||
#undef WIN32
|
||||
|
||||
/* Define to a string defining your target. ($target in configure.in) */
|
||||
#undef CPU_VENDOR_OS
|
||||
|
||||
|
||||
/* Define to the full path of the ifconfig program */
|
||||
#undef IFCONFIG
|
||||
|
||||
/* Define to the full path of the route program */
|
||||
#undef ROUTE
|
||||
|
||||
/* Define to the full path of the arp program */
|
||||
#undef ARP
|
||||
|
||||
|
||||
/*
|
||||
* These four below should definately be done away with!!
|
||||
*/
|
||||
|
||||
/* Define if your target OS is a BSD derivative */
|
||||
#undef BSD
|
||||
|
||||
/* Define if your target OS is Linux */
|
||||
#undef LINUX
|
||||
|
||||
/* Define if your target OS is SunOS 5.x */
|
||||
#undef SOLARIS
|
||||
|
||||
/* Define if your target OS is BSD/OS 4.x */
|
||||
#undef BSDI
|
||||
|
||||
|
||||
/*
|
||||
* What are these???
|
||||
*/
|
||||
|
||||
/* ? */
|
||||
#undef USE_IFALIAS
|
||||
|
||||
/* Define if you wish to use the bpf interface */
|
||||
#undef USE_BPF
|
||||
|
||||
/* Define if you wish to use the dlpi interface */
|
||||
#undef USE_DLPI
|
||||
|
||||
/* ? */
|
||||
#undef USE_SOCKET
|
||||
|
||||
|
||||
/* Define if prototypes for malloc can be found in stdlib.h */
|
||||
#undef STDLIB_MALLOC
|
||||
|
||||
/* Define if your include files defines a prototype for sys_errlist[] */
|
||||
#undef HAVE_SYS_ERRLIST
|
||||
|
||||
/* This isn't used anywhere (that I could find) so I don't know what it means*/
|
||||
#undef IFCONFIG_REQUIRES_DOWN_ADDRESS
|
||||
|
||||
/* Define if your OS have broken cmsg fields in the msghdr struct (Linux) */
|
||||
#undef BROKEN_CMSG_FIELDS
|
||||
|
||||
/* Define if sockaddr structure has sa_len member */
|
||||
#undef SA_LEN_IN_SOCKADDR
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Provide a common way to refer to ints with specific size. (Is this
|
||||
* used everywhere?) The names used are {u_,}int{8,16,32,64}_t unless
|
||||
* they are defined in sys/types.h they are defined in ints.h using
|
||||
* the BIT macros. e.g. if int8_t isn't defined in sys/types.h int8_t
|
||||
* is typedef:ed to BIT8.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Define to a basic signed type that is 8 bits in size */
|
||||
#undef BIT8
|
||||
|
||||
/* Define to a basic signed type that is 16 bits in size */
|
||||
#undef BIT16
|
||||
|
||||
/* Define to a basic signed type that is 32 bits in size */
|
||||
#undef BIT32
|
||||
|
||||
/* Define to a basic signed type that is 64 bits in size */
|
||||
#undef BIT64
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int8_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int8_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int16_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int16_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int32_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int32_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int64_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int64_t
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* */
|
||||
#undef ETHER_HEADER_USES_ETHER_ADDR
|
||||
|
||||
/* */
|
||||
#undef HAVE_DLIOCRAW
|
||||
|
||||
/* */
|
||||
#undef HAVE_ETHERADDRL
|
||||
|
||||
/* */
|
||||
#undef HAVE_ETHER_ADDR_LEN
|
||||
|
||||
/* */
|
||||
#undef HAVE_MSGHDR_MSG_CONTROL
|
||||
|
||||
/* */
|
||||
#undef HAVE_SIOCGARP
|
||||
|
||||
/* */
|
||||
#undef HAVE_SIOCGIFCONF
|
||||
|
||||
/* */
|
||||
#undef HAVE_SIOCGIFHWADDR
|
||||
|
||||
/* */
|
||||
#undef HAVE_arpreq
|
||||
|
||||
/* */
|
||||
#undef HAVE_caddr_t
|
||||
|
||||
/* */
|
||||
#undef HAVE_ether_header
|
||||
|
||||
/* */
|
||||
#undef HAVE_ethhdr
|
||||
|
||||
/* */
|
||||
#undef HAVE_ifnet
|
||||
|
||||
/* */
|
||||
#undef HAVE_in_addr
|
||||
|
||||
/* */
|
||||
#undef HAVE_sockaddr
|
||||
|
||||
/* */
|
||||
#undef HAVE_sockaddr_dl
|
||||
|
||||
/* */
|
||||
#undef HAVE_sockaddr_in
|
||||
|
||||
/* */
|
||||
#undef NEED_HAVE_sockaddr_dl
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* I have yet to figure out what all this need_* stuff is for, shouldn't
|
||||
* it suffice with using have_* ???
|
||||
*/
|
||||
|
||||
/* */
|
||||
#undef NEED_LINUX_SOCKIOS_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NETINET_IF_ETHER_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NETINET_IN_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_ETHERNET_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_IF_ARP_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_IF_DL_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_IF_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_BITYPES_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_DLPI_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_ETHERNET_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_SOCKETIO_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_SOCKET_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_SOCKIO_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_TYPES_H
|
||||
|
||||
/* if we have the openssl with engine support */
|
||||
#undef HAVE_SSL_ENGINE
|
||||
/* ... with Rainbow patches */
|
||||
#undef HAVE_RAINBOW_PATCHES
|
||||
/* ... with Rainbow's libswift */
|
||||
#undef HAVE_SWIFT
|
||||
/* ... with one of our HW checks */
|
||||
#undef HAVE_LOCAL_ENGINE_SETUP
|
||||
#undef HAVE_SSL_HW_CHECK
|
||||
/* ... with patch to get cfg password from card */
|
||||
#undef HAVE_ENGINE_GET_PASSWORD
|
||||
/* ... with our buffer patches */
|
||||
#undef HAVE_SSL_BUFFER_CB
|
||||
|
||||
/* */
|
||||
#undef ISD_SYSTEM_VSN
|
||||
|
||||
/* eventpoll */
|
||||
#undef HAVE_KPOLL
|
||||
|
||||
/* Have/use netfilter (a.k.a. iptables) */
|
||||
#undef HAVE_NETFILTER
|
||||
|
||||
/* Non-standard support for "non-local connect()" in Linux 2.4 */
|
||||
#undef HAVE_NONLOCAL_CONNECT
|
||||
|
||||
/* atomic asmebler op in asm/atomic.h ? */
|
||||
#undef HAVE_ATOMIC_OPS
|
||||
|
||||
|
||||
@BOTTOM@
|
||||
|
||||
#endif /* WIN32 */
|
||||
#endif /* _CONFIG_H_ */
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
dnl ----------------------------------------------------------------------
|
||||
dnl
|
||||
dnl BT_MSG_CONTROL checks for msg_control member in msghdr and that
|
||||
dnl the cmsg fields aren't broken...
|
||||
dnl
|
||||
|
||||
AC_DEFUN(BT_MSG_CONTROL,
|
||||
[
|
||||
AC_CACHE_CHECK([for msg_control member in msghdr],
|
||||
bt_cv_have_msghdr_msg_control,
|
||||
[AC_TRY_COMPILE([#include <sys/types.h>
|
||||
#include <sys/socket.h>],
|
||||
[struct msghdr msg;
|
||||
msg.msg_control;],
|
||||
bt_cv_have_msghdr_msg_control=yes, bt_cv_have_msghdr_msg_control=no)])
|
||||
if test $bt_cv_have_msghdr_msg_control = yes; then
|
||||
AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL)
|
||||
fi
|
||||
|
||||
if test $bt_cv_have_msghdr_msg_control = yes; then
|
||||
AC_MSG_CHECKING(for broken CMSG_FIELDS)
|
||||
case "$target_os" in
|
||||
linux*)
|
||||
AC_DEFINE(BROKEN_CMSG_FIELDS)
|
||||
AC_MSG_RESULT(yes)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT(no)
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
])
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,319 @@
|
|||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
|
||||
/*
|
||||
* This file contains prototypes for config.h.in which autoheader
|
||||
* could not figure by itself. I.e. if you write your own test which
|
||||
* defines a macro you will probably have to put it here, but if you
|
||||
* use any standard test AC_* it will be detected by autoheader.
|
||||
*/
|
||||
|
||||
#undef WIN32
|
||||
|
||||
/* Define to a string defining your target. ($target in configure.in) */
|
||||
#undef CPU_VENDOR_OS
|
||||
|
||||
|
||||
/* Define to the full path of the ifconfig program */
|
||||
#undef IFCONFIG
|
||||
|
||||
/* Define to the full path of the route program */
|
||||
#undef ROUTE
|
||||
|
||||
/* Define to the full path of the arp program */
|
||||
#undef ARP
|
||||
|
||||
|
||||
/*
|
||||
* These four below should definately be done away with!!
|
||||
*/
|
||||
|
||||
/* Define if your target OS is a BSD derivative */
|
||||
#undef BSD
|
||||
|
||||
/* Define if your target OS is Linux */
|
||||
#undef LINUX
|
||||
|
||||
/* Define if your target OS is SunOS 5.x */
|
||||
#undef SOLARIS
|
||||
|
||||
/* Define if your target OS is BSD/OS 4.x */
|
||||
#undef BSDI
|
||||
|
||||
|
||||
/*
|
||||
* What are these???
|
||||
*/
|
||||
|
||||
/* ? */
|
||||
#undef USE_IFALIAS
|
||||
|
||||
/* Define if you wish to use the bpf interface */
|
||||
#undef USE_BPF
|
||||
|
||||
/* Define if you wish to use the dlpi interface */
|
||||
#undef USE_DLPI
|
||||
|
||||
/* ? */
|
||||
#undef USE_SOCKET
|
||||
|
||||
|
||||
/* Define if prototypes for malloc can be found in stdlib.h */
|
||||
#undef STDLIB_MALLOC
|
||||
|
||||
/* Define if your include files defines a prototype for sys_errlist[] */
|
||||
#undef HAVE_SYS_ERRLIST
|
||||
|
||||
/* This isn't used anywhere (that I could find) so I don't know what it means*/
|
||||
#undef IFCONFIG_REQUIRES_DOWN_ADDRESS
|
||||
|
||||
/* Define if your OS have broken cmsg fields in the msghdr struct (Linux) */
|
||||
#undef BROKEN_CMSG_FIELDS
|
||||
|
||||
/* Define if sockaddr structure has sa_len member */
|
||||
#undef SA_LEN_IN_SOCKADDR
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Provide a common way to refer to ints with specific size. (Is this
|
||||
* used everywhere?) The names used are {u_,}int{8,16,32,64}_t unless
|
||||
* they are defined in sys/types.h they are defined in ints.h using
|
||||
* the BIT macros. e.g. if int8_t isn't defined in sys/types.h int8_t
|
||||
* is typedef:ed to BIT8.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Define to a basic signed type that is 8 bits in size */
|
||||
#undef BIT8
|
||||
|
||||
/* Define to a basic signed type that is 16 bits in size */
|
||||
#undef BIT16
|
||||
|
||||
/* Define to a basic signed type that is 32 bits in size */
|
||||
#undef BIT32
|
||||
|
||||
/* Define to a basic signed type that is 64 bits in size */
|
||||
#undef BIT64
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int8_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int8_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int16_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int16_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int32_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int32_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_int64_t
|
||||
|
||||
/* Define if sys/types.h defines this type */
|
||||
#undef HAVE_u_int64_t
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* */
|
||||
#undef ETHER_HEADER_USES_ETHER_ADDR
|
||||
|
||||
/* */
|
||||
#undef HAVE_DLIOCRAW
|
||||
|
||||
/* */
|
||||
#undef HAVE_ETHERADDRL
|
||||
|
||||
/* */
|
||||
#undef HAVE_ETHER_ADDR_LEN
|
||||
|
||||
/* */
|
||||
#undef HAVE_MSGHDR_MSG_CONTROL
|
||||
|
||||
/* */
|
||||
#undef HAVE_SIOCGARP
|
||||
|
||||
/* */
|
||||
#undef HAVE_SIOCGIFCONF
|
||||
|
||||
/* */
|
||||
#undef HAVE_SIOCGIFHWADDR
|
||||
|
||||
/* */
|
||||
#undef HAVE_arpreq
|
||||
|
||||
/* */
|
||||
#undef HAVE_caddr_t
|
||||
|
||||
/* */
|
||||
#undef HAVE_ether_header
|
||||
|
||||
/* */
|
||||
#undef HAVE_ethhdr
|
||||
|
||||
/* */
|
||||
#undef HAVE_ifnet
|
||||
|
||||
/* */
|
||||
#undef HAVE_in_addr
|
||||
|
||||
/* */
|
||||
#undef HAVE_sockaddr
|
||||
|
||||
/* */
|
||||
#undef HAVE_sockaddr_dl
|
||||
|
||||
/* */
|
||||
#undef HAVE_sockaddr_in
|
||||
|
||||
/* */
|
||||
#undef NEED_HAVE_sockaddr_dl
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* I have yet to figure out what all this need_* stuff is for, shouldn't
|
||||
* it suffice with using have_* ???
|
||||
*/
|
||||
|
||||
/* */
|
||||
#undef NEED_LINUX_SOCKIOS_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NETINET_IF_ETHER_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NETINET_IN_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_ETHERNET_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_IF_ARP_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_IF_DL_H
|
||||
|
||||
/* */
|
||||
#undef NEED_NET_IF_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_BITYPES_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_DLPI_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_ETHERNET_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_SOCKETIO_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_SOCKET_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_SOCKIO_H
|
||||
|
||||
/* */
|
||||
#undef NEED_SYS_TYPES_H
|
||||
|
||||
/* if we have the openssl with engine support */
|
||||
#undef HAVE_SSL_ENGINE
|
||||
/* ... with Rainbow patches */
|
||||
#undef HAVE_RAINBOW_PATCHES
|
||||
/* ... with Rainbow's libswift */
|
||||
#undef HAVE_SWIFT
|
||||
/* ... with one of our HW checks */
|
||||
#undef HAVE_LOCAL_ENGINE_SETUP
|
||||
#undef HAVE_SSL_HW_CHECK
|
||||
/* ... with patch to get cfg password from card */
|
||||
#undef HAVE_ENGINE_GET_PASSWORD
|
||||
/* ... with our buffer patches */
|
||||
#undef HAVE_SSL_BUFFER_CB
|
||||
|
||||
/* */
|
||||
#undef ISD_SYSTEM_VSN
|
||||
|
||||
/* eventpoll */
|
||||
#undef HAVE_KPOLL
|
||||
|
||||
/* Have/use netfilter (a.k.a. iptables) */
|
||||
#undef HAVE_NETFILTER
|
||||
|
||||
/* Non-standard support for "non-local connect()" in Linux 2.4 */
|
||||
#undef HAVE_NONLOCAL_CONNECT
|
||||
|
||||
/* atomic asmebler op in asm/atomic.h ? */
|
||||
#undef HAVE_ATOMIC_OPS
|
||||
|
||||
|
||||
|
||||
/* Description */
|
||||
#undef DARWIN
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#undef HAVE_MALLOC_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
#endif /* WIN32 */
|
||||
#endif /* _CONFIG_H_ */
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
|||
AC_INIT
|
||||
|
||||
dnl work out who the cpu, vendor and OS are
|
||||
AC_CANONICAL_SYSTEM
|
||||
AC_DEFINE_UNQUOTED(CPU_VENDOR_OS, "$target")
|
||||
|
||||
dnl Programs
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PATH_PROG(ERL, erl)
|
||||
AC_PATH_PROG(ERLC, erlc)
|
||||
ERLBINDIR=`dirname $ERL` ; ERLBINDIR=`dirname $ERLBINDIR`/lib/erlang/bin
|
||||
|
||||
ERLDIR=`awk -F= '/ROOTDIR=/ { print [$]2; exit; }' $ERL`
|
||||
AC_SUBST(ERL)
|
||||
AC_SUBST(ERLC)
|
||||
AC_SUBST(ERLBINDIR)
|
||||
AC_SUBST(ERLDIR)
|
||||
|
||||
if test ! -d "$ERLDIR" ; then
|
||||
AC_MSG_ERROR([Broken Erlang installation, $ERLDIR does not exist!])
|
||||
fi
|
||||
|
||||
dnl C header files
|
||||
|
||||
AC_CONFIG_HEADER(config.h:config.h.in)
|
||||
|
||||
AC_CHECK_HEADERS(malloc.h)
|
||||
|
||||
BT_MSG_CONTROL
|
||||
|
||||
case "$target_os" in
|
||||
*cygwin*)
|
||||
:
|
||||
dnl fix this later
|
||||
;;
|
||||
linux*)
|
||||
AC_DEFINE(LINUX)
|
||||
LD_SHARED="ld -shared"
|
||||
;;
|
||||
*bsd*)
|
||||
AC_DEFINE(BSD)
|
||||
LD_SHARED="ld -Bshareable"
|
||||
;;
|
||||
*solaris*)
|
||||
AC_DEFINE(SOLARIS)
|
||||
LD_SHARED="ld -G"
|
||||
;;
|
||||
*darwin*)
|
||||
AC_DEFINE([DARWIN], [], [Description])
|
||||
LD_SHARED="cc -bundle -flat_namespace -undefined suppress"
|
||||
;;
|
||||
*)
|
||||
LD_SHARED="ld -shared"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(LD_SHARED)
|
||||
|
||||
|
||||
dnl libnsl and libsocket tests borrowed from ethereal's autoconf scheme.
|
||||
dnl # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
|
||||
dnl # to get the SysV transport functions.
|
||||
dnl # chad@anasazi.com says the Pyramid MIS-ES running DC/OSx (SVR4)
|
||||
dnl # needs -lnsl.
|
||||
dnl # The nsl library prevents programs from opening the X display
|
||||
dnl # on Irix 5.2, according to dickey@clark.net.
|
||||
AC_CHECK_FUNC(gethostbyname, ,
|
||||
AC_CHECK_LIB(nsl, gethostbyname, NSL_LIBS="-lnsl"))
|
||||
AC_SUBST(NSL_LIBS)
|
||||
dnl # lieder@skyler.mavd.honeywell.com says without -lsocket,
|
||||
dnl # socket/setsockopt and other routines are undefined under SCO ODT
|
||||
dnl # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary
|
||||
dnl # on later versions), says simon@lia.di.epfl.ch: it contains
|
||||
dnl # gethostby* variants that don't use the nameserver (or something).
|
||||
dnl # -lsocket must be given before -lnsl if both are needed.
|
||||
dnl # We assume that if connect needs -lnsl, so does gethostbyname.
|
||||
AC_CHECK_FUNC(connect, ,
|
||||
AC_CHECK_LIB(socket, connect, SOCKET_LIBS="-lsocket",
|
||||
AC_MSG_ERROR(Function 'socket' not found.), $NSL_LIBS))
|
||||
AC_SUBST(SOCKET_LIBS)
|
||||
|
||||
dnl
|
||||
dnl End.
|
||||
|
||||
AC_OUTPUT(include.mk)
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
## -*- makefile -*-
|
||||
|
||||
######################################################################
|
||||
## C
|
||||
|
||||
CC := @CC@
|
||||
CFLAGS := @CFLAGS@ @DEFS@
|
||||
|
||||
LD_SHARED := @LD_SHARED@
|
||||
|
||||
######################################################################
|
||||
## Erlang
|
||||
|
||||
ERL = @ERL@
|
||||
ERLC = @ERLC@
|
||||
ERLDIR = @ERLDIR@
|
||||
|
||||
ERL_C_INCLUDE_DIR := $(ERLDIR)/usr/include
|
||||
|
||||
|
||||
ERLC_FLAGS := -W
|
||||
|
||||
ifndef no_debug_info
|
||||
ERLC_FLAGS += +debug_info
|
||||
endif
|
||||
|
||||
ifdef debug
|
||||
ERLC_FLAGS += -Ddebug
|
||||
endif
|
||||
|
||||
EBIN_DIR := ../ebin
|
||||
DOC_DIR := ../doc
|
||||
EMULATOR := beam
|
||||
|
||||
ERL_SOURCES := $(wildcard *.erl)
|
||||
ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl)
|
||||
ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR))
|
||||
ERL_DOCUMENTS := $(ERL_SOURCES:%.erl=$(DOC_DIR)/%.html)
|
||||
|
||||
# Hmm, don't know if you are supposed to like this better... ;-)
|
||||
APPSCRIPT = '$$vsn=shift; $$mods=""; while(@ARGV){ $$_=shift; s/^([A-Z].*)$$/\'\''$$1\'\''/; $$mods.=", " if $$mods; $$mods .= $$_; } while(<>) { s/%VSN%/$$vsn/; s/%MODULES%/$$mods/; print; }'
|
||||
|
||||
|
||||
../ebin/%.app: %.app.src ../vsn.mk Makefile
|
||||
perl -e $(APPSCRIPT) "$(VSN)" $(MODULES) < $< > $@
|
||||
|
||||
../ebin/%.appup: %.appup
|
||||
cp $< $@
|
||||
|
||||
|
||||
$(EBIN_DIR)/%.$(EMULATOR): %.erl
|
||||
$(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $<
|
||||
|
||||
# generate documentation with edoc:
|
||||
# this is still not the proper way to do it, but it works
|
||||
# (see the wumpus application for an example)
|
||||
|
||||
$(DOC_DIR)/%.html: %.erl
|
||||
${ERL} -noshell \
|
||||
-pa ../../syntax_tools/ebin \
|
||||
-pa ../../edoc/ebin \
|
||||
-pa ../../xmerl/ebin \
|
||||
-pa ../../ucs/ebin \
|
||||
-run edoc file $< -run init stop
|
||||
mv *.html $(DOC_DIR)
|
||||
|
||||
# C linking items
|
||||
|
||||
NSL_LIBS = @NSL_LIBS@
|
||||
SOCKET_LIBS = @SOCKET_LIBS@
|
||||
|
||||
# Miscellaneous
|
||||
|
||||
GROFF = @GROFF@
|
||||
PS2PDF = @PS2PDF@
|
|
@ -0,0 +1,250 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5 (mit/util/scripts/install.sh).
|
||||
#
|
||||
# Copyright 1991 by the Massachusetts Institute of Technology
|
||||
#
|
||||
# Permission to use, copy, modify, distribute, and sell this software and its
|
||||
# documentation for any purpose is hereby granted without fee, provided that
|
||||
# the above copyright notice appear in all copies and that both that
|
||||
# copyright notice and this permission notice appear in supporting
|
||||
# documentation, and that the name of M.I.T. not be used in advertising or
|
||||
# publicity pertaining to distribution of the software without specific,
|
||||
# written prior permission. M.I.T. makes no representations about the
|
||||
# suitability of this software for any purpose. It is provided "as is"
|
||||
# without express or implied warranty.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch. It can only install one file at a time, a restriction
|
||||
# shared with many OS's install programs.
|
||||
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
transformbasename=""
|
||||
transform_arg=""
|
||||
instcmd="$mvprog"
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=""
|
||||
dst=""
|
||||
dir_arg=""
|
||||
|
||||
while [ x"$1" != x ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd="$stripprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
*) if [ x"$src" = x ]
|
||||
then
|
||||
src=$1
|
||||
else
|
||||
# this colon is to work around a 386BSD /bin/sh bug
|
||||
:
|
||||
dst=$1
|
||||
fi
|
||||
shift
|
||||
continue;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]; then
|
||||
dst=$src
|
||||
src=""
|
||||
|
||||
if [ -d $dst ]; then
|
||||
instcmd=:
|
||||
else
|
||||
instcmd=mkdir
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
then
|
||||
true
|
||||
else
|
||||
echo "install: $src does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# If destination is a directory, append the input filename; if your system
|
||||
# does not like double slashes in filenames, you may need to add some logic
|
||||
|
||||
if [ -d $dst ]
|
||||
then
|
||||
dst="$dst"/`basename $src`
|
||||
else
|
||||
true
|
||||
fi
|
||||
fi
|
||||
|
||||
## this sed command emulates the dirname command
|
||||
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if [ ! -d "$dstdir" ]; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-${defaultIFS}}"
|
||||
|
||||
oIFS="${IFS}"
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
IFS="${oIFS}"
|
||||
|
||||
pathcomp=''
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
pathcomp="${pathcomp}${1}"
|
||||
shift
|
||||
|
||||
if [ ! -d "${pathcomp}" ] ;
|
||||
then
|
||||
$mkdirprog "${pathcomp}"
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
pathcomp="${pathcomp}/"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]
|
||||
then
|
||||
$doit $instcmd $dst &&
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||
else
|
||||
|
||||
# If we're going to rename the final executable, determine the name now.
|
||||
|
||||
if [ x"$transformarg" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
sed $transformarg`$transformbasename
|
||||
fi
|
||||
|
||||
# don't allow the sed command to completely eliminate the filename
|
||||
|
||||
if [ x"$dstfile" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
|
||||
$doit $instcmd $src $dsttmp &&
|
||||
|
||||
trap "rm -f ${dsttmp}" 0 &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits
|
||||
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
|
||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
|
||||
fi &&
|
||||
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
include ../config/include.mk
|
||||
|
||||
include ../vsn.mk
|
||||
VSN=$(FD_SERVER_VSN)
|
||||
|
||||
ifeq ($(TYPE),debug)
|
||||
DEBUG_FLAGS = -Ddebug
|
||||
else
|
||||
DEBUG_FLAGS =
|
||||
endif
|
||||
|
||||
MODULES=bfile
|
||||
|
||||
EBIN = ../ebin
|
||||
EBIN_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(EBIN)/bfile.app
|
||||
|
||||
|
||||
all: $(EBIN_FILES)
|
||||
|
||||
debug:
|
||||
$(MAKE) DEBUG=-DDEBUG
|
||||
clean:
|
||||
rm -rf $(EBIN_FILES)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{application,bfile,
|
||||
[{description,"Fast FILE interface"},
|
||||
{vsn,"%VSN%"},
|
||||
{modules,[%MODULES%]},
|
||||
{applications,[kernel,stdlib]},
|
||||
{env, []},
|
||||
{mod,{bfile,[]}}]}.
|
|
@ -0,0 +1,224 @@
|
|||
%%%
|
||||
%%% File : bfile.erl
|
||||
%%% Author : Claes Wikstrom <klacke@kaja.klacke.net>
|
||||
%%% Purpose : Interface to stdio buffered FILE io
|
||||
%%% Created : 22 Nov 1999 by Claes Wikstrom <klacke@kaja.klacke.net>
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(bfile).
|
||||
-vsn("$Revision: 1.1 $ ").
|
||||
-author('klacke@kaja.klacke.net').
|
||||
|
||||
-export([
|
||||
load_driver/0,
|
||||
fopen/2,
|
||||
fclose/1,
|
||||
fread/2,
|
||||
fwrite/2,
|
||||
feof/1,
|
||||
ferror/1,
|
||||
set_linebuf_size/2,
|
||||
fseek/3,
|
||||
ftell/1,
|
||||
ftruncate/1,
|
||||
fflush/1,
|
||||
frewind/1,
|
||||
fgetc/1,
|
||||
fungetc/2,
|
||||
fgets/1,
|
||||
gets/1,
|
||||
pwrite/3,
|
||||
pread/3
|
||||
]).
|
||||
|
||||
|
||||
%% Opcodes
|
||||
-define(OPEN, $o).
|
||||
-define(CLOSE, $c).
|
||||
-define(READ, $r).
|
||||
-define(WRITE, $w).
|
||||
-define(SEEK, $s).
|
||||
-define(TELL, $t).
|
||||
-define(TRUNCATE, $T).
|
||||
-define(FLUSH, $f).
|
||||
-define(OEOF, $e).
|
||||
-define(ERROR, $E).
|
||||
-define(GETC, $g).
|
||||
-define(GETS, $G).
|
||||
-define(GETS2, $2).
|
||||
-define(SET_LINEBUF_SIZE, $S).
|
||||
-define(UNGETC, $u).
|
||||
|
||||
|
||||
%% ret codes
|
||||
-define(VALUE, $v).
|
||||
-define(FLINE, $L).
|
||||
-define(OK, $o).
|
||||
-define(I32, $O).
|
||||
-define(NOLINE,$N).
|
||||
-define(FERROR, $E).
|
||||
-define(REOF, $x).
|
||||
|
||||
-define(int32(X),
|
||||
[((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff,
|
||||
((X) bsr 8) band 16#ff, (X) band 16#ff]).
|
||||
%% Bytes to unsigned
|
||||
-define(u32(X3,X2,X1,X0),
|
||||
(((X3) bsl 24) bor ((X2) bsl 16) bor ((X1) bsl 8) bor (X0))).
|
||||
%% Bytes to signed
|
||||
-define(i32(X3,X2,X1,X0),
|
||||
(?u32(X3,X2,X1,X0) -
|
||||
(if (X3) > 127 -> 16#100000000; true -> 0 end))).
|
||||
|
||||
load_driver() ->
|
||||
Dir = filename:join([filename:dirname(code:which(bfile)),"..", "priv"]),
|
||||
erl_ddll:load_driver(Dir, "FILE_drv").
|
||||
|
||||
|
||||
%% Flags = "r" | "w" | ... etc, see fopen(3)
|
||||
%% Ret: {ok, Fd} | {error, Reason}
|
||||
|
||||
fopen(Fname, Flags) ->
|
||||
P = open_port({spawn, 'FILE_drv'}, [binary]),
|
||||
Res = erlang_port_control(P, ?OPEN, [Fname, 0, Flags, 0]),
|
||||
case decode(Res) of
|
||||
ok ->
|
||||
{ok, {bfile, P}};
|
||||
Err ->
|
||||
unlink(P),
|
||||
exit(P, die),
|
||||
Err
|
||||
end.
|
||||
|
||||
%% void()
|
||||
fclose({bfile, Fd}) ->
|
||||
unlink(Fd),
|
||||
catch erlang:port_close(Fd).
|
||||
|
||||
%% {ok, #Bin} | {error, Reason} | eof
|
||||
fread({bfile, Fd}, Sz) ->
|
||||
Res = erlang_port_control(Fd, ?READ, ?int32(Sz)),
|
||||
decode(Res).
|
||||
|
||||
%% ok | {error, Reason}
|
||||
fwrite({bfile, Fd}, IoList) ->
|
||||
Res = erlang_port_control(Fd, ?WRITE, IoList),
|
||||
decode(Res).
|
||||
|
||||
%% ok | {error, Reason}
|
||||
pwrite(BFd, Pos, IoList) ->
|
||||
case fseek(BFd, Pos, seek_set) of
|
||||
ok ->
|
||||
fwrite(BFd, IoList);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% {ok, #Bin} | {error, Reason} | eof
|
||||
pread(BFd, Pos, Sz) ->
|
||||
case fseek(BFd, Pos, seek_set) of
|
||||
ok ->
|
||||
fread(BFd, Sz);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% bool
|
||||
feof({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?OEOF, []),
|
||||
bool(decode(Res)).
|
||||
|
||||
%% bool
|
||||
ferror({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?ERROR, []),
|
||||
bool(decode(Res)).
|
||||
|
||||
|
||||
%% void()
|
||||
set_linebuf_size({bfile, Fd}, Sz) ->
|
||||
Res = erlang_port_control(Fd, ?SET_LINEBUF_SIZE, ?int32(Sz)),
|
||||
decode(Res).
|
||||
|
||||
%% Whence == seek_set | seek_cur || seek_end
|
||||
%% ok | {error, Reason}
|
||||
fseek({bfile, Fd}, Offs, Whence) ->
|
||||
Res = erlang_port_control(Fd, ?SEEK, [?int32(Offs), whence_enc(Whence)]),
|
||||
decode(Res).
|
||||
|
||||
%% {ok, Int} | {error, Reason}
|
||||
ftell({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?TELL, []),
|
||||
decode(Res).
|
||||
|
||||
%% ok | {error, Reason}
|
||||
ftruncate({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?TRUNCATE, []),
|
||||
decode(Res).
|
||||
|
||||
%% ok | {error, Reason}
|
||||
fflush({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?FLUSH, []),
|
||||
decode(Res).
|
||||
|
||||
%% ok | {error, Reason}
|
||||
frewind(BFd) ->
|
||||
fseek(BFd, 0, seek_set).
|
||||
|
||||
%% {ok, Char} | {error, Reason} | eof
|
||||
fgetc({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?GETC, []),
|
||||
decode(Res).
|
||||
|
||||
%% ok | {error, Reason}
|
||||
fungetc({bfile, Fd}, Char) ->
|
||||
Res = erlang_port_control(Fd, ?UNGETC, [Char]),
|
||||
decode(Res).
|
||||
|
||||
%% {line, #Bin} | {noline, #Bin} | {error, Reason} | eof
|
||||
%% including newline
|
||||
fgets({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?GETS, []),
|
||||
decode(Res).
|
||||
|
||||
%% {line, #Bin} | {noline, #Bin} | {error, Reason} | eof
|
||||
%% not including newline
|
||||
gets({bfile, Fd}) ->
|
||||
Res = erlang_port_control(Fd, ?GETS2, []),
|
||||
decode(Res).
|
||||
|
||||
|
||||
whence_enc(seek_set) ->
|
||||
1;
|
||||
whence_enc(seek_cur) ->
|
||||
2;
|
||||
whence_enc(seek_end) ->
|
||||
3.
|
||||
|
||||
|
||||
bool({ok, 1}) ->
|
||||
true;
|
||||
bool({ok, 0}) ->
|
||||
false.
|
||||
|
||||
|
||||
decode(Res) ->
|
||||
case Res of
|
||||
<<?VALUE, Bin/binary>> ->
|
||||
{ok, Bin};
|
||||
<<?FLINE, Bin/binary>> ->
|
||||
{line, Bin};
|
||||
<<?OK>> ->
|
||||
ok;
|
||||
<<?I32, X1, X2, X3, X4>> ->
|
||||
{ok, ?i32(X1, X2, X3, X4)};
|
||||
<<?NOLINE, Bin/binary>> ->
|
||||
{noline, Bin};
|
||||
<<?FERROR, Err/binary>> ->
|
||||
{error, list_to_atom(binary_to_list(Err))};
|
||||
<<?REOF>> ->
|
||||
eof
|
||||
end.
|
||||
|
||||
erlang_port_control(P, C, Data) ->
|
||||
erlang:port_control(P, C, Data).
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
-module(read).
|
||||
-export([start/1, start/2]).
|
||||
|
||||
scan_file(F, Readsize, Total) ->
|
||||
Rd = bfile:fread(F, Readsize),
|
||||
case Rd of
|
||||
{ok, Bin} -> scan_file(F, Readsize, size(Bin)+Total);
|
||||
eof -> Total
|
||||
end.
|
||||
scan_file(F, Readsize) -> scan_file(F, Readsize, 0).
|
||||
|
||||
start(File, Readsize) ->
|
||||
bfile:load_driver(),
|
||||
{ok, F} = bfile:fopen(File, "r"),
|
||||
T = scan_file(F, Readsize),
|
||||
io:format("read ~p bytes~n", [T]),
|
||||
bfile:fclose(F).
|
||||
start(File) ->
|
||||
start(File, 512*1024).
|
|
@ -0,0 +1,18 @@
|
|||
-module(readold).
|
||||
-export([start/1, start/2]).
|
||||
|
||||
scan_file(F, Readsize, Total) ->
|
||||
Rd = file:read(F, Readsize),
|
||||
case Rd of
|
||||
{ok, Bin} -> scan_file(F, Readsize, size(Bin)+Total);
|
||||
eof -> Total
|
||||
end.
|
||||
scan_file(F, Readsize) -> scan_file(F, Readsize, 0).
|
||||
|
||||
start(File, Readsize) ->
|
||||
{ok, F} = file:open(File, [raw, binary, read]),
|
||||
T = scan_file(F, Readsize),
|
||||
io:format("read ~p bytes~n", [T]),
|
||||
file:close(F).
|
||||
start(File) ->
|
||||
start(File, 512*1024).
|
|
@ -0,0 +1,17 @@
|
|||
-module(write).
|
||||
-export([start/2, start/3]).
|
||||
|
||||
dump_file(F, Data, 0) ->
|
||||
ok;
|
||||
dump_file(F, Data, N) ->
|
||||
bfile:fwrite(F, Data),
|
||||
dump_file(F, Data, N - 1).
|
||||
|
||||
start(File, Data, N) ->
|
||||
bfile:load_driver(),
|
||||
{ok, F} = bfile:fopen(File, "w"),
|
||||
dump_file(F, Data, N),
|
||||
bfile:fclose(F).
|
||||
start(File, N) ->
|
||||
Data = list_to_binary(lists:duplicate(1000000, 10)),
|
||||
start(File, Data, N).
|
|
@ -0,0 +1,16 @@
|
|||
-module(writeold).
|
||||
-export([start/2, start/3]).
|
||||
|
||||
dump_file(F, Data, 0) ->
|
||||
ok;
|
||||
dump_file(F, Data, N) ->
|
||||
file:write(F, Data),
|
||||
dump_file(F, Data, N - 1).
|
||||
|
||||
start(File, Data, N) ->
|
||||
{ok, F} = file:open(File, [raw, binary, write]),
|
||||
dump_file(F, Data, N),
|
||||
file:close(F).
|
||||
start(File, N) ->
|
||||
Data = list_to_binary(lists:duplicate(1000000, 10)),
|
||||
start(File, Data, N).
|
|
@ -0,0 +1 @@
|
|||
BFILE_VSN=1.0
|
|
@ -0,0 +1,381 @@
|
|||
%%%-------------------------------------------------------------------
|
||||
%% @copyright Process One 2008-2009
|
||||
%% @author Geoff Cant <geoff.cant@process-one.net>
|
||||
%% @author Geoff Cant <nem@erlang.geek.nz>
|
||||
%% @version {@vsn}, {@date} {@time}
|
||||
%% @doc DNS Lookup and utility functions.
|
||||
%%
|
||||
%% Provides a programmer-friendly API for a number of undocumented OTP
|
||||
%% dns lookup, resolution, caching and configuration functions.
|
||||
%%
|
||||
%% Also provides utility functions for performing lookups while
|
||||
%% bypassing local resolver caching, finding closest parent zones,
|
||||
%% parsing `resolv.conf' files, simplifying #dns_rec{} answers and more.
|
||||
%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(dns).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
%% API
|
||||
-export([lookup/2
|
||||
,lookup/3
|
||||
,lookup/4
|
||||
,lookup/5
|
||||
,lookup/7
|
||||
,lookup_cache/3
|
||||
,cache_lookup/1
|
||||
,simplify/1]).
|
||||
|
||||
-export([find_soa/1
|
||||
,nameservers/1
|
||||
,nameservers/2
|
||||
,nameserver_address/1
|
||||
,direct_lookup/3
|
||||
,info/2
|
||||
,to_proplist/1
|
||||
,parse_resolv/1
|
||||
,domain_exists/1
|
||||
,expand_options/1
|
||||
,resolvers/0
|
||||
]).
|
||||
|
||||
-include_lib("kernel/src/inet_dns.hrl").
|
||||
-include_lib("kernel/include/inet.hrl").
|
||||
|
||||
%% @type query_class() = in.
|
||||
%% An atom with the name of a DNS query class. `in' is the only class
|
||||
%% used (and probably tested) in the OTP lookup code.
|
||||
|
||||
%% @type query_type() = a | cname | soa | mx | ns | srv | any.
|
||||
%% An atom with the name of a DNS query type. This list is not
|
||||
%% exhaustive.
|
||||
|
||||
%% @type simple_rr() = {Name::string(), query_type(), RRdata::term(), [simple_rr_info()]}.
|
||||
%% A simplified version of #dns_rr{} resource records.
|
||||
|
||||
%% @type simple_rr_info() = {ttl, TimeToLive::integer()} | {class, query_class()}.
|
||||
|
||||
%% @type ip_address() = {integer(),integer(),integer(),integer()}.
|
||||
|
||||
%% @type address() = ip_address() | {ip_address(), port_no()} |
|
||||
%% string() | #dns_rr{}.
|
||||
|
||||
%% @type port_no() = integer().
|
||||
|
||||
%% @type resolver() = {ip_address(), port_no()}.
|
||||
|
||||
%% @type query_options() = [query_option()].
|
||||
%% A proplist of configuration options for lookup behaviour.
|
||||
|
||||
%% @type query_option() = {read_cache, bool()} |
|
||||
%% {write_cache, bool()} |
|
||||
%% {timeout, TimeOut::integer()} |
|
||||
%% {servers, [address()]} |
|
||||
%% {class, query_class()} |
|
||||
%% defaults | read_cached.
|
||||
%% <ul>
|
||||
%% <li>`read_cache' - if `true' (default: `false'), return results from the `inet_db' cache
|
||||
%% before making a query over the network.</li>
|
||||
%% <li>`write_cache' - if `true' (default: `false'), write successful query result RRs to
|
||||
%% the `inet_db' cache</li>
|
||||
%% <li>`timeout' - number of milliseconds (default: 5000) to wait for a response from a
|
||||
%% nameserver.</li>
|
||||
%% <li>`servers' - a list of nameservers to query for the RRs. Defaults
|
||||
%% to `inet_db:res_option(nameserver)' which is usually the nameservers
|
||||
%% specified in '/etc/resolv.conf'</li>
|
||||
%% <li>`class' - record class to query for (default: `in').</li>
|
||||
%% <li>`defaults' - shorthand for
|
||||
%% <code>
|
||||
%% [{servers, dns:resolvers()},
|
||||
%% {read_cache, false}, {write_cache, false},
|
||||
%% {timeout, timer:seconds(5)}, {class, in}]
|
||||
%% </code>
|
||||
%% </li>
|
||||
%% <li>`read_cached' - shorthand for
|
||||
%% <code>
|
||||
%% [{servers, dns:resolvers()},
|
||||
%% {read_cache, true}, {write_cache, false},
|
||||
%% {timeout, timer:seconds(5)}, {class, in}]
|
||||
%% </code>
|
||||
%% </li>
|
||||
%% </ul>
|
||||
|
||||
%%====================================================================
|
||||
%% RR lookup API
|
||||
%%====================================================================
|
||||
|
||||
%% @spec lookup(Name::string(), query_type()) -> #dns_res{}
|
||||
%% @doc Query for a DNS record of Type for Name. Uses reasonable default
|
||||
%% options for class (`in') timeouts (5s), caching (no caching) and
|
||||
%% nameservers (`inet_db' defaults).
|
||||
%% @end
|
||||
%% @equiv lookup(Name, Type, [defaults])
|
||||
lookup(Name, Type)
|
||||
when is_list(Name), is_atom(Type) ->
|
||||
lookup(Name, Type, [defaults]).
|
||||
|
||||
%% @spec lookup(Name::string(), query_type(), query_options()) -> #dns_res{}
|
||||
%% @doc Query for a DNS record of Type for Name. Takes a variety of
|
||||
%% query options.
|
||||
%% @end
|
||||
lookup(Name, Type, Options)
|
||||
when is_list(Name), is_atom(Type),
|
||||
is_list(Options) ->
|
||||
lookup2(Name, Type, expand_options(Options)).
|
||||
|
||||
%% @private
|
||||
lookup2(Name, Type, Options) ->
|
||||
Timeout = proplists:get_value(timeout, Options, timer:seconds(5)),
|
||||
Servers = [nameserver_address(R)
|
||||
|| R <- proplists:get_value(servers, Options, resolvers())],
|
||||
Class = proplists:get_value(class, Options, in),
|
||||
ReadCache = proplists:get_value(read_cache, Options, false),
|
||||
WriteCache = proplists:get_value(write_cache, Options, false),
|
||||
lookup(Name, Class, Type, Servers, Timeout, ReadCache, WriteCache).
|
||||
|
||||
%% @spec lookup(Name::string(), query_type(),
|
||||
%% NameServers, Timeout::integer()) -> #dns_res{}
|
||||
%% where NameServers = [resolver()]
|
||||
%% @doc Query the given Nameservers for a DNS record of Type (class `in') for
|
||||
%% Name. Takes a Timeout in milliseconds. Doesn't read or write the
|
||||
%% `inet_db' RR cache.
|
||||
%% @equiv lookup(Name, in, Type, Servers, Timeout, false, false)
|
||||
lookup(Name, Type, Servers, Timeout)
|
||||
when is_list(Name), is_atom(Type),
|
||||
is_list(Servers), is_integer(Timeout) ->
|
||||
lookup(Name, in, Type, Servers, Timeout).
|
||||
|
||||
%% @spec lookup(Name::string(), query_class(), query_type(),
|
||||
%% [resolver()], Timeout::integer()) -> #dns_res{}
|
||||
%% @doc Query the given Nameservers for a DNS record of Class, Type for
|
||||
%% Name. Takes a Timeout in milliseconds. Doesn't read or write the
|
||||
%% `inet_db' RR cache.
|
||||
%% @equiv lookup(Name, Class, Type, Servers, Timeout, false, false)
|
||||
lookup(Name, Class, Type, Servers, Timeout)
|
||||
when is_list(Name), is_atom(Class), is_atom(Type),
|
||||
is_list(Servers), is_integer(Timeout) ->
|
||||
lookup(Name, Class, Type, Servers, Timeout, false, false).
|
||||
|
||||
%% @spec lookup(Name::string(), class(), query_type(),
|
||||
%% Servers::[address()], Timeout::integer(),
|
||||
%% ReadCache::bool(), WriteCache::bool()) -> #dns_res{}
|
||||
%% @doc Query the given Nameservers for a DNS record of Class, Type for
|
||||
%% Name. Takes a Timeout in milliseconds. Returns results from the
|
||||
%% `inet_db' RR cache if available and ReadCache is `true'. Writes
|
||||
%% successful query answers to the cache if WriteCache is `true'.
|
||||
%% @end
|
||||
%% Query inet_db cache.
|
||||
lookup(Name, Class = in, Type, Servers, Timeout,
|
||||
_ReadCache = true, WriteCache) ->
|
||||
case lookup_cache(Class, Name, Type) of
|
||||
{ok, Answer} -> {ok, Answer};
|
||||
{error, nxdomain} ->
|
||||
lookup(Name, Class, Type, Servers, Timeout, false, WriteCache)
|
||||
end;
|
||||
%% Perform DNS lookup and write results to inet_db cache
|
||||
lookup(Name, Class, Type, Servers, Timeout,
|
||||
_ReadCache, _WriteCache = true) ->
|
||||
case lookup(Name, Class, Type, Servers, Timeout, false, false) of
|
||||
{ok, Rec} -> cache_lookup(Rec), Rec;
|
||||
Else -> Else
|
||||
end;
|
||||
%% Lookup via DNS
|
||||
lookup(Name, Class, Type, Servers, Timeout,
|
||||
_ReadCache = false, _WriteCache = false) ->
|
||||
inet_res:nnslookup(Name, Class, Type, Servers, Timeout).
|
||||
|
||||
%% @private
|
||||
%% Process options list
|
||||
expand_options(Opts) ->
|
||||
proplists:expand(option_expansions(), Opts).
|
||||
%% @private
|
||||
option_expansions() ->
|
||||
[{defaults, [{servers, resolvers()},
|
||||
{timeout, timer:seconds(5)},
|
||||
{read_cache, false},
|
||||
{write_cache, false}]}
|
||||
,{read_cached, [{servers, resolvers()},
|
||||
{timeout, timer:seconds(5)},
|
||||
{read_cache, true},
|
||||
{write_cache, false}]}
|
||||
].
|
||||
|
||||
%% @spec simplify(Result) -> {error, Reason::term()} | [simple_rr()]
|
||||
%% where Result = {ok, #dns_rec{}} | {error, Reason::term()}
|
||||
%% @doc Simplify the records returned from lookup to fixed-format
|
||||
%% tuples instead of records.
|
||||
%% @end
|
||||
simplify({ok, #dns_rec{anlist=A}}) ->
|
||||
{ok, [{Name, Type, Data, [{ttl, TTL},{class,Class}]}
|
||||
|| #dns_rr{domain=Name,class=Class,
|
||||
type=Type,data=Data,
|
||||
ttl=TTL} <- A]};
|
||||
simplify(E) -> E.
|
||||
|
||||
%%====================================================================
|
||||
%% Cache API
|
||||
%%====================================================================
|
||||
|
||||
%% @spec lookup_cache(query_class(), Name::string(), query_type()) ->
|
||||
%% {ok, #dns_rec{}} | {error, Reason::term()}
|
||||
%% @doc Query `inet_db' RR cache. Converts the `inet_db' `#hostent{}' respone
|
||||
%% into a fake `#dns_rec{}'.
|
||||
lookup_cache(Class, Name, Type) ->
|
||||
case inet_db:getbyname(Name, Type) of
|
||||
{ok, #hostent{h_addr_list=Answers}} ->
|
||||
%% Convert hostents to fake dns records
|
||||
RRs = [#dns_rr{domain=Name, class=Class,
|
||||
type=Type, ttl=cached,
|
||||
data=D}
|
||||
|| D <- Answers],
|
||||
{ok, #dns_rec{header=cached, anlist=RRs}};
|
||||
Else -> Else
|
||||
end.
|
||||
|
||||
%% @spec cache_lookup(#dns_rec{}) -> ok
|
||||
%% @doc Store dns answer resource records in inet_db cache
|
||||
cache_lookup(#dns_rec{anlist=RRs}) ->
|
||||
lists:foreach(fun inet_db:add_rr/1, RRs),
|
||||
ok.
|
||||
|
||||
%%====================================================================
|
||||
%% Utility API
|
||||
%%====================================================================
|
||||
|
||||
%% @spec resolvers() -> [resolver()]
|
||||
%% @doc Returns a list of name servers that can be used as recursive
|
||||
%% resolvers. Resolvers taken from the `inet_db' `namserver' setting.
|
||||
resolvers() ->
|
||||
inet_db:res_option(nameserver).
|
||||
|
||||
%% @spec find_soa(Name::string()) -> {ok, {SoaName::string(), #dns_rr{}}} |
|
||||
%% {error, Reason::term()}
|
||||
%% @doc Recursively climb the ancestry of a domain name to find the
|
||||
%% most specific SOA. (Closest domain delegation/authority)
|
||||
%% Given `foo.bar.baz.com' would try `foo.bar.baz.com' then `bar.baz.com'
|
||||
%% then `baz.com' and so on.
|
||||
find_soa(Name) ->
|
||||
Components = string:tokens(Name, "."),
|
||||
find_soa2(Components).
|
||||
|
||||
%% @private
|
||||
find_soa2([]) ->
|
||||
{error, nxdomain};
|
||||
find_soa2(Components) ->
|
||||
Name = string:join(Components,"."),
|
||||
case lookup(Name, soa) of
|
||||
{ok, #dns_rec{anlist=[RR = #dns_rr{domain=Name,type=soa}]}} ->
|
||||
{ok, {Name, RR}};
|
||||
{error, nxdomain} ->
|
||||
find_soa2(tl(Components));
|
||||
Else -> Else
|
||||
end.
|
||||
|
||||
%% @spec domain_exists(DomainName::string()) -> bool()
|
||||
%% @doc Returns `true' if a name exists and has an SOA record.
|
||||
domain_exists(Name) ->
|
||||
case lookup(Name, soa) of
|
||||
{ok, #dns_rec{anlist=[#dns_rr{domain=Name,type=soa}]}} ->
|
||||
true;
|
||||
{error, nxdomain} ->
|
||||
false;
|
||||
Else -> erlang:error(Else)
|
||||
end.
|
||||
|
||||
%% @spec parse_resolv(FileName::string()) -> [ip_address()]
|
||||
%% @doc Returns a list of nameservers from a POSIX style `resolv.conf' file.
|
||||
parse_resolv(File) ->
|
||||
{ok, Rs} = inet_parse:resolv(File),
|
||||
[NS || {nameserver,NS} <- Rs].
|
||||
|
||||
%% @spec nameservers(Domain::string()) -> [resolver()]
|
||||
%% @equiv nameservers(Domain, [read_cached])
|
||||
nameservers(Domain) -> nameservers(Domain, [read_cached]).
|
||||
|
||||
%% @spec nameservers(Domain::string(), query_options()) -> [resolver()]
|
||||
%% @doc Retrieve the names and addresses of the authoritative
|
||||
%% nameservers for Domain. Will perform one NS lookup if the
|
||||
%% resolving nameserver returns the address records for the queried
|
||||
%% records, otherwise will perform 1 + N lookups (N = number of NS
|
||||
%% records for Domain). Takes a list of query option that will be used
|
||||
%% for NS lookups.
|
||||
nameservers(Domain, Options) ->
|
||||
case lookup(Domain, ns, Options) of
|
||||
E = {error, _} -> E;
|
||||
{ok, #dns_rec{anlist=RRs, arlist=AR}} ->
|
||||
Names = [Name
|
||||
|| #dns_rr{data=Name,type=ns,domain=D} <- RRs,
|
||||
D =:= Domain],
|
||||
Additional = [Addr || #dns_rr{data=Addr,type=a,domain=Name} <- AR,
|
||||
lists:member(Name, Names)],
|
||||
if length(Additional) > 0 ->
|
||||
[nameserver_address(Addr) || Addr <- Additional];
|
||||
true ->
|
||||
[nameserver_address(RR) || RR <- RRs]
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec nameserver_address(address()) -> resolver() | error
|
||||
%% @doc
|
||||
%% Convert a variety of nameserver address specifications into a
|
||||
%% host-port tuple for use with `gen_udp' and `inet_res'.
|
||||
%% @end
|
||||
%% @todo Should take/respect query options instead of calling inet:getaddr/2.
|
||||
nameserver_address(IP = {_, _, _, _}) -> {IP,53};
|
||||
nameserver_address(Addr = {{_, _, _, _}, _}) -> Addr;
|
||||
nameserver_address(Name) when is_list(Name) ->
|
||||
case inet:getaddr(Name, inet) of
|
||||
{ok, IP} -> {IP, 53};
|
||||
_ -> error
|
||||
end;
|
||||
nameserver_address(#dns_rr{data=Name,type=ns}) ->
|
||||
nameserver_address(Name).
|
||||
|
||||
%% @spec direct_lookup(Name::string(), query_type(), Domain::string())
|
||||
%% -> #dns_res{}
|
||||
%% @doc Lookup the given Name/Type against the authoritative NSs for
|
||||
%% Domain. This will bypass nxdomain caching and should result in
|
||||
%% quicker recognition of changed/added records.
|
||||
%% This is mainly intended for checking if people have setup a set of
|
||||
%% DNS records correctly (say for a white-label hosting service).
|
||||
direct_lookup(Name, Type, Domain) ->
|
||||
Servers = [{A,53} || {_NS, A} <- nameservers(Domain)],
|
||||
lookup(Name, Type, [{servers, Servers}]).
|
||||
|
||||
%%====================================================================
|
||||
%% inet record manipulation and access API
|
||||
%%====================================================================
|
||||
|
||||
%% @private
|
||||
info(Field, Rec) ->
|
||||
Fields = fields(Rec),
|
||||
FieldIdx = lists:zip(Fields, lists:seq(2,length(Fields)+1)),
|
||||
element(proplists:get_value(Field,FieldIdx), Rec).
|
||||
|
||||
%% @private
|
||||
fields(#dns_rec{}) -> fields(dns_rec);
|
||||
fields(dns_rec) -> record_info(fields, dns_rec);
|
||||
fields(#dns_rr{}) -> fields(dns_rr);
|
||||
fields(dns_rr) -> record_info(fields, dns_rr).
|
||||
|
||||
%% @private
|
||||
to_proplist(R) ->
|
||||
Keys = fields(R),
|
||||
Values = tl(tuple_to_list(R)),
|
||||
lists:zip(Keys,Values).
|
||||
|
||||
%%====================================================================
|
||||
%% Unit Tests
|
||||
%%====================================================================
|
||||
%% @todo Add more unit tests.
|
||||
|
||||
%% @private
|
||||
nameserver_address_test_() ->
|
||||
T = [{{1,2,3,4}, {{1,2,3,4},53}},
|
||||
{{{1,2,3,4},53}, {{1,2,3,4},53}},
|
||||
{#dns_rr{data={1,2,3,4},type=ns}, {{1,2,3,4},53}},
|
||||
{"localhost", {{127,0,0,1},53}},
|
||||
{#dns_rr{data="localhost",type=ns}, {{127,0,0,1},53}}],
|
||||
[ ?_assertMatch(C when C =:= B, nameserver_address(A))
|
||||
|| {A,B} <- T].
|
|
@ -0,0 +1,5 @@
|
|||
ejabberd-dev is an helper module containing include files from the ejabberd
|
||||
project.
|
||||
|
||||
This should allow to develop or build ejabberd modules without a complete
|
||||
development version of ejabberd (for example with a binary version of ejabberd).
|
|
@ -1,10 +1,6 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : s3_util.erl
|
||||
%%% Usage : S3 URL Generation and Signing
|
||||
%%% Author : Roman Hargrave <roman@hargrave.info>
|
||||
%%% Purpose : Signing AWS Requests. Intended for S3-CS use.
|
||||
%%% Created : 24 Aug 2022 by Roman Hargrave <roman@hargrave.info>
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -16,13 +12,25 @@
|
|||
%%% 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(aws_auth, {access_key_id :: binary(),
|
||||
access_key :: binary(),
|
||||
region :: binary()}).
|
||||
-record(adhoc_request, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
action,
|
||||
xdata,
|
||||
others}).
|
||||
|
||||
-define(AWS_SERVICE_S3, <<"s3">>).
|
||||
-record(adhoc_response, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
status,
|
||||
defaultaction = "",
|
||||
actions = [],
|
||||
notes = [],
|
||||
elements = []}).
|
|
@ -0,0 +1,64 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% This macro returns a string of the ejabberd version running, e.g. "2.3.4"
|
||||
%% If the ejabberd application description isn't loaded, returns atom: undefined
|
||||
-define(VERSION, element(2, application:get_key(ejabberd,vsn))).
|
||||
|
||||
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
|
||||
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
|
||||
-define(MYLANG, ejabberd_config:get_global_option(language)).
|
||||
|
||||
-define(MSGS_DIR, "msgs").
|
||||
-define(CONFIG_PATH, "ejabberd.cfg").
|
||||
-define(LOG_PATH, "ejabberd.log").
|
||||
|
||||
-define(EJABBERD_URI, "http://www.process-one.net/en/ejabberd/").
|
||||
|
||||
-define(S2STIMEOUT, 600000).
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
-record(scram, {storedkey, serverkey, salt, iterationcount}).
|
||||
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
|
||||
|
||||
%% ---------------------------------
|
||||
%% Logging mechanism
|
||||
|
||||
%% Print in standard output
|
||||
-define(PRINT(Format, Args),
|
||||
io:format(Format, Args)).
|
||||
|
||||
-define(DEBUG(Format, Args),
|
||||
ejabberd_logger:debug_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(INFO_MSG(Format, Args),
|
||||
ejabberd_logger:info_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(WARNING_MSG(Format, Args),
|
||||
ejabberd_logger:warning_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(ERROR_MSG(Format, Args),
|
||||
ejabberd_logger:error_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
||||
-define(CRITICAL_MSG(Format, Args),
|
||||
ejabberd_logger:critical_msg(?MODULE,?LINE,Format, Args)).
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(ejabberd_commands, {name, tags = [],
|
||||
desc = "", longdesc = "",
|
||||
module, function,
|
||||
args = [], result = rescode}).
|
||||
|
||||
%% @type ejabberd_commands() = #ejabberd_commands{
|
||||
%% name = atom(),
|
||||
%% tags = [atom()],
|
||||
%% desc = string(),
|
||||
%% longdesc = string(),
|
||||
%% module = atom(),
|
||||
%% function = atom(),
|
||||
%% args = [aterm()],
|
||||
%% result = rterm()
|
||||
%% }.
|
||||
%% desc: Description of the command
|
||||
%% args: Describe the accepted arguments.
|
||||
%% This way the function that calls the command can format the
|
||||
%% arguments before calling.
|
||||
|
||||
%% @type atype() = integer | string | {tuple, [aterm()]} | {list, aterm()}.
|
||||
%% Allowed types for arguments are integer, string, tuple and list.
|
||||
|
||||
%% @type rtype() = integer | string | atom | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple.
|
||||
%% A rtype is either an atom or a tuple with two elements.
|
||||
|
||||
%% @type aterm() = {Name::atom(), Type::atype()}.
|
||||
%% An argument term is a tuple with the term name and the term type.
|
||||
|
||||
%% @type rterm() = {Name::atom(), Type::rtype()}.
|
||||
%% A result term is a tuple with the term name and the term type.
|
|
@ -0,0 +1,28 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(config, {key, value}).
|
||||
-record(local_config, {key, value}).
|
||||
-record(state, {opts = [],
|
||||
hosts = [],
|
||||
override_local = false,
|
||||
override_global = false,
|
||||
override_acls = false}).
|
|
@ -0,0 +1,25 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
-define(STATUS_BADRPC, 3).
|
|
@ -0,0 +1,336 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
|
||||
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
|
||||
-define(NS_VCARD, "vcard-temp").
|
||||
-define(NS_VCARD_UPDATE, "vcard-temp:x:update").
|
||||
-define(NS_AUTH, "jabber:iq:auth").
|
||||
-define(NS_AUTH_ERROR, "jabber:iq:auth:error").
|
||||
-define(NS_REGISTER, "jabber:iq:register").
|
||||
-define(NS_SEARCH, "jabber:iq:search").
|
||||
-define(NS_ROSTER, "jabber:iq:roster").
|
||||
-define(NS_ROSTER_VER, "urn:xmpp:features:rosterver").
|
||||
-define(NS_PRIVACY, "jabber:iq:privacy").
|
||||
-define(NS_BLOCKING, "urn:xmpp:blocking").
|
||||
-define(NS_PRIVATE, "jabber:iq:private").
|
||||
-define(NS_VERSION, "jabber:iq:version").
|
||||
-define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete
|
||||
-define(NS_TIME, "urn:xmpp:time").
|
||||
-define(NS_LAST, "jabber:iq:last").
|
||||
-define(NS_XDATA, "jabber:x:data").
|
||||
-define(NS_IQDATA, "jabber:iq:data").
|
||||
-define(NS_DELAY91, "jabber:x:delay"). % TODO: Remove once XEP-0091 is Obsolete
|
||||
-define(NS_DELAY, "urn:xmpp:delay").
|
||||
-define(NS_EXPIRE, "jabber:x:expire").
|
||||
-define(NS_EVENT, "jabber:x:event").
|
||||
-define(NS_CHATSTATES, "http://jabber.org/protocol/chatstates").
|
||||
-define(NS_XCONFERENCE, "jabber:x:conference").
|
||||
-define(NS_STATS, "http://jabber.org/protocol/stats").
|
||||
-define(NS_MUC, "http://jabber.org/protocol/muc").
|
||||
-define(NS_MUC_USER, "http://jabber.org/protocol/muc#user").
|
||||
-define(NS_MUC_ADMIN, "http://jabber.org/protocol/muc#admin").
|
||||
-define(NS_MUC_OWNER, "http://jabber.org/protocol/muc#owner").
|
||||
-define(NS_MUC_UNIQUE, "http://jabber.org/protocol/muc#unique").
|
||||
-define(NS_PUBSUB, "http://jabber.org/protocol/pubsub").
|
||||
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
|
||||
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
|
||||
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
|
||||
-define(NS_PUBSUB_ERRORS,"http://jabber.org/protocol/pubsub#errors").
|
||||
-define(NS_PUBSUB_NODE_CONFIG, "http://jabber.org/protocol/pubsub#node_config").
|
||||
-define(NS_PUBSUB_SUB_OPTIONS, "http://jabber.org/protocol/pubsub#subscribe_options").
|
||||
-define(NS_PUBSUB_SUB_AUTH, "http://jabber.org/protocol/pubsub#subscribe_authorization").
|
||||
-define(NS_PUBSUB_GET_PENDING, "http://jabber.org/protocol/pubsub#get-pending").
|
||||
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
|
||||
-define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams").
|
||||
-define(NS_ADMIN, "http://jabber.org/protocol/admin").
|
||||
-define(NS_SERVERINFO, "http://jabber.org/network/serverinfo").
|
||||
|
||||
-define(NS_RSM, "http://jabber.org/protocol/rsm").
|
||||
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
|
||||
|
||||
-define(NS_STREAM, "http://etherx.jabber.org/streams").
|
||||
|
||||
-define(NS_STANZAS, "urn:ietf:params:xml:ns:xmpp-stanzas").
|
||||
-define(NS_STREAMS, "urn:ietf:params:xml:ns:xmpp-streams").
|
||||
|
||||
-define(NS_TLS, "urn:ietf:params:xml:ns:xmpp-tls").
|
||||
-define(NS_SASL, "urn:ietf:params:xml:ns:xmpp-sasl").
|
||||
-define(NS_SESSION, "urn:ietf:params:xml:ns:xmpp-session").
|
||||
-define(NS_BIND, "urn:ietf:params:xml:ns:xmpp-bind").
|
||||
|
||||
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
|
||||
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
|
||||
-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
|
||||
-define(NS_FEATURE_MSGOFFLINE, "msgoffline").
|
||||
|
||||
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
|
||||
|
||||
-define(NS_CAPS, "http://jabber.org/protocol/caps").
|
||||
-define(NS_SHIM, "http://jabber.org/protocol/shim").
|
||||
-define(NS_ADDRESS, "http://jabber.org/protocol/address").
|
||||
|
||||
%% CAPTCHA related NSes.
|
||||
-define(NS_OOB, "jabber:x:oob").
|
||||
-define(NS_CAPTCHA, "urn:xmpp:captcha").
|
||||
-define(NS_MEDIA, "urn:xmpp:media-element").
|
||||
-define(NS_BOB, "urn:xmpp:bob").
|
||||
|
||||
% TODO: remove "code" attribute (currently it used for backward-compatibility)
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
{xmlelement, "error",
|
||||
[{"code", Code}, {"type", Type}],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}).
|
||||
|
||||
-define(ERR_BAD_FORMAT,
|
||||
?STANZA_ERROR("406", "modify", "bad-format")).
|
||||
-define(ERR_BAD_REQUEST,
|
||||
?STANZA_ERROR("400", "modify", "bad-request")).
|
||||
-define(ERR_CONFLICT,
|
||||
?STANZA_ERROR("409", "cancel", "conflict")).
|
||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
?STANZA_ERROR("501", "cancel", "feature-not-implemented")).
|
||||
-define(ERR_FORBIDDEN,
|
||||
?STANZA_ERROR("403", "auth", "forbidden")).
|
||||
-define(ERR_GONE,
|
||||
?STANZA_ERROR("302", "modify", "gone")).
|
||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
||||
?STANZA_ERROR("500", "wait", "internal-server-error")).
|
||||
-define(ERR_ITEM_NOT_FOUND,
|
||||
?STANZA_ERROR("404", "cancel", "item-not-found")).
|
||||
-define(ERR_JID_MALFORMED,
|
||||
?STANZA_ERROR("400", "modify", "jid-malformed")).
|
||||
-define(ERR_NOT_ACCEPTABLE,
|
||||
?STANZA_ERROR("406", "modify", "not-acceptable")).
|
||||
-define(ERR_NOT_ALLOWED,
|
||||
?STANZA_ERROR("405", "cancel", "not-allowed")).
|
||||
-define(ERR_NOT_AUTHORIZED,
|
||||
?STANZA_ERROR("401", "auth", "not-authorized")).
|
||||
-define(ERR_PAYMENT_REQUIRED,
|
||||
?STANZA_ERROR("402", "auth", "payment-required")).
|
||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
||||
?STANZA_ERROR("404", "wait", "recipient-unavailable")).
|
||||
-define(ERR_REDIRECT,
|
||||
?STANZA_ERROR("302", "modify", "redirect")).
|
||||
-define(ERR_REGISTRATION_REQUIRED,
|
||||
?STANZA_ERROR("407", "auth", "registration-required")).
|
||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
?STANZA_ERROR("404", "cancel", "remote-server-not-found")).
|
||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
||||
?STANZA_ERROR("504", "wait", "remote-server-timeout")).
|
||||
-define(ERR_RESOURCE_CONSTRAINT,
|
||||
?STANZA_ERROR("500", "wait", "resource-constraint")).
|
||||
-define(ERR_SERVICE_UNAVAILABLE,
|
||||
?STANZA_ERROR("503", "cancel", "service-unavailable")).
|
||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
||||
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
|
||||
?STANZA_ERROR("401", "cancel", "unexpected-request")).
|
||||
%-define(ERR_,
|
||||
% ?STANZA_ERROR("", "", "")).
|
||||
|
||||
-define(STANZA_ERRORT(Code, Type, Condition, Lang, Text),
|
||||
{xmlelement, "error",
|
||||
[{"code", Code}, {"type", Type}],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []},
|
||||
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(ERRT_BAD_FORMAT(Lang, Text),
|
||||
?STANZA_ERRORT("406", "modify", "bad-format", Lang, Text)).
|
||||
-define(ERRT_BAD_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT("400", "modify", "bad-request", Lang, Text)).
|
||||
-define(ERRT_CONFLICT(Lang, Text),
|
||||
?STANZA_ERRORT("409", "cancel", "conflict", Lang, Text)).
|
||||
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
||||
?STANZA_ERRORT("501", "cancel", "feature-not-implemented", Lang, Text)).
|
||||
-define(ERRT_FORBIDDEN(Lang, Text),
|
||||
?STANZA_ERRORT("403", "auth", "forbidden", Lang, Text)).
|
||||
-define(ERRT_GONE(Lang, Text),
|
||||
?STANZA_ERRORT("302", "modify", "gone", Lang, Text)).
|
||||
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STANZA_ERRORT("500", "wait", "internal-server-error", Lang, Text)).
|
||||
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT("404", "cancel", "item-not-found", Lang, Text)).
|
||||
-define(ERRT_JID_MALFORMED(Lang, Text),
|
||||
?STANZA_ERRORT("400", "modify", "jid-malformed", Lang, Text)).
|
||||
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
||||
?STANZA_ERRORT("406", "modify", "not-acceptable", Lang, Text)).
|
||||
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
||||
?STANZA_ERRORT("405", "cancel", "not-allowed", Lang, Text)).
|
||||
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STANZA_ERRORT("401", "auth", "not-authorized", Lang, Text)).
|
||||
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT("402", "auth", "payment-required", Lang, Text)).
|
||||
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT("404", "wait", "recipient-unavailable", Lang, Text)).
|
||||
-define(ERRT_REDIRECT(Lang, Text),
|
||||
?STANZA_ERRORT("302", "modify", "redirect", Lang, Text)).
|
||||
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT("407", "auth", "registration-required", Lang, Text)).
|
||||
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT("404", "cancel", "remote-server-not-found", Lang, Text)).
|
||||
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
||||
?STANZA_ERRORT("504", "wait", "remote-server-timeout", Lang, Text)).
|
||||
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
||||
?STANZA_ERRORT("500", "wait", "resource-constraint", Lang, Text)).
|
||||
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT("503", "cancel", "service-unavailable", Lang, Text)).
|
||||
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT("407", "auth", "subscription-required", Lang, Text)).
|
||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT("400", "wait", "unexpected-request", Lang, Text)).
|
||||
|
||||
% Auth stanza errors
|
||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, "No resource provided")).
|
||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, "Illegal resource format")).
|
||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
||||
|
||||
|
||||
-define(STREAM_ERROR(Condition),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR("bad-format")).
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR("bad-namespace-prefix")).
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR("conflict")).
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR("connection-timeout")).
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR("host-gone")).
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
?STREAM_ERROR("host-unknown")).
|
||||
-define(SERR_IMPROPER_ADDRESSING,
|
||||
?STREAM_ERROR("improper-addressing")).
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR("internal-server-error")).
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR("invalid-from")).
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR("invalid-id")).
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR("invalid-namespace")).
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR("invalid-xml")).
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR("not-authorized")).
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR("policy-violation")).
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR("remote-connection-failed")).
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR("resource-constraint")).
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR("restricted-xml")).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERR_SEE_OTHER_HOST,
|
||||
?STREAM_ERROR("see-other-host")).
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR("system-shutdown")).
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR("unsupported-encoding")).
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR("unsupported-stanza-type")).
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
?STREAM_ERROR("unsupported-version")).
|
||||
-define(SERR_XML_NOT_WELL_FORMED,
|
||||
?STREAM_ERROR("xml-not-well-formed")).
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
||||
?STREAM_ERRORT("bad-format", Lang, Text)).
|
||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
||||
?STREAM_ERRORT("connection-timeout", Lang, Text)).
|
||||
-define(SERRT_HOST_GONE(Lang, Text),
|
||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
||||
?STREAM_ERRORT("host-unknown", Lang, Text)).
|
||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STREAM_ERRORT("internal-server-error", Lang, Text)).
|
||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
||||
-define(SERRT_INVALID_ID(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-id", Lang, Text)).
|
||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
||||
-define(SERRT_INVALID_XML(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-xml", Lang, Text)).
|
||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
||||
?STREAM_ERRORT("policy-violation", Lang, Text)).
|
||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
||||
?STREAM_ERRORT("resource-constraint", Lang, Text)).
|
||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
||||
?STREAM_ERRORT("system-shutdown", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
||||
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)).
|
||||
%-define(SERRT_(Lang, Text),
|
||||
% ?STREAM_ERRORT("", Lang, Text)).
|
||||
|
||||
|
||||
-record(jid, {user, server, resource,
|
||||
luser, lserver, lresource}).
|
||||
|
||||
-record(iq, {id = "",
|
||||
type,
|
||||
xmlns = "",
|
||||
lang = "",
|
||||
sub_el}).
|
||||
|
||||
-record(rsm_in, {max, direction, id, index}).
|
||||
-record(rsm_out, {count, index, first, last}).
|
|
@ -0,0 +1,90 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(MAX_USERS_DEFAULT, 200).
|
||||
|
||||
-define(SETS, gb_sets).
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(lqueue, {queue, len, max}).
|
||||
|
||||
-record(config, {title = "",
|
||||
description = "",
|
||||
allow_change_subj = true,
|
||||
allow_query_users = true,
|
||||
allow_private_messages = true,
|
||||
allow_private_messages_from_visitors = anyone,
|
||||
allow_visitor_status = true,
|
||||
allow_visitor_nickchange = true,
|
||||
public = true,
|
||||
public_list = true,
|
||||
persistent = false,
|
||||
moderated = true,
|
||||
captcha_protected = false,
|
||||
members_by_default = true,
|
||||
members_only = false,
|
||||
allow_user_invites = false,
|
||||
password_protected = false,
|
||||
password = "",
|
||||
anonymous = true,
|
||||
allow_voice_requests = true,
|
||||
voice_request_min_interval = 1800,
|
||||
max_users = ?MAX_USERS_DEFAULT,
|
||||
logging = false,
|
||||
captcha_whitelist = ?SETS:empty()
|
||||
}).
|
||||
|
||||
-record(user, {jid,
|
||||
nick,
|
||||
role,
|
||||
last_presence}).
|
||||
|
||||
-record(activity, {message_time = 0,
|
||||
presence_time = 0,
|
||||
message_shaper,
|
||||
presence_shaper,
|
||||
message,
|
||||
presence}).
|
||||
|
||||
-record(state, {room,
|
||||
host,
|
||||
server_host,
|
||||
mod,
|
||||
access,
|
||||
jid,
|
||||
config = #config{},
|
||||
users = ?DICT:new(),
|
||||
last_voice_request_time = treap:empty(),
|
||||
robots = ?DICT:new(),
|
||||
nicks = ?DICT:new(),
|
||||
affiliations = ?DICT:new(),
|
||||
history,
|
||||
subject = "",
|
||||
subject_author = "",
|
||||
just_created = false,
|
||||
activity = treap:empty(),
|
||||
room_shaper,
|
||||
room_queue = queue:new()}).
|
||||
|
||||
-record(muc_online_users, {us,
|
||||
resource,
|
||||
room,
|
||||
host}).
|
|
@ -0,0 +1,196 @@
|
|||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%%
|
||||
%%% copyright 2006-2013 ProcessOne
|
||||
%%%
|
||||
%%% This file contains pubsub types definition.
|
||||
%%% ====================================================================
|
||||
|
||||
%% -------------------------------
|
||||
%% Pubsub constants
|
||||
-define(ERR_EXTENDED(E,C), mod_pubsub:extended_error(E,C)).
|
||||
|
||||
%% The actual limit can be configured with mod_pubsub's option max_items_node
|
||||
-define(MAXITEMS, 10).
|
||||
|
||||
%% this is currently a hard limit.
|
||||
%% Would be nice to have it configurable.
|
||||
-define(MAX_PAYLOAD_SIZE, 60000).
|
||||
|
||||
%% -------------------------------
|
||||
%% Pubsub types
|
||||
|
||||
%% @type hostPubsub() = string().
|
||||
%% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be
|
||||
%% <tt>"pubsub.localhost"</tt>.</p>
|
||||
|
||||
%% @type hostPEP() = {User, Server, Resource}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Resource = [].
|
||||
%% <p>For example, it can be :
|
||||
%% ```{"bob", "example.org", []}'''.</p>
|
||||
|
||||
%% @type host() = hostPubsub() | hostPEP().
|
||||
|
||||
%% @type nodeId() = binary().
|
||||
%% <p>A node is defined by a list of its ancestors. The last element is the name
|
||||
%% of the current node. For example:
|
||||
%% ```<<"/home/localhost/user">>'''</p>
|
||||
|
||||
%% @type nodeIdx() = integer().
|
||||
|
||||
%% @type itemId() = string().
|
||||
|
||||
%% @type subId() = string().
|
||||
|
||||
%% @type payload() = [#xmlelement{} | #xmlcdata{}].
|
||||
|
||||
%% @type stanzaError() = #xmlelement{}.
|
||||
%% Example:
|
||||
%% ```{xmlelement, "error",
|
||||
%% [{"code", Code}, {"type", Type}],
|
||||
%% [{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}'''
|
||||
%% @type pubsubIQResponse() = #xmlelement{}.
|
||||
%% Example:
|
||||
%% ```{xmlelement, "pubsub",
|
||||
%% [{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
%% [{xmlelement, "affiliations", [],
|
||||
%% []}]}'''
|
||||
|
||||
%% @type nodeOption() = {Option, Value}
|
||||
%% Option = atom()
|
||||
%% Value = term().
|
||||
%% Example:
|
||||
%% ```{deliver_payloads, true}'''
|
||||
|
||||
%% @type nodeType() = string().
|
||||
%% <p>The <tt>nodeType</tt> is a string containing the name of the PubSub
|
||||
%% plugin to use to manage a given node. For example, it can be
|
||||
%% <tt>"flat"</tt>, <tt>"hometree"</tt> or <tt>"blog"</tt>.</p>
|
||||
|
||||
%% @type jid() = {jid, User, Server, Resource, LUser, LServer, LResource}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Resource = string()
|
||||
%% LUser = string()
|
||||
%% LServer = string()
|
||||
%% LResource = string().
|
||||
|
||||
%% @type ljid() = {User, Server, Resource}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Resource = string().
|
||||
|
||||
%% @type affiliation() = 'none' | 'owner' | 'publisher' | 'publish-only' | 'member' | 'outcast'.
|
||||
|
||||
%% @type subscription() = 'none' | 'pending' | 'unconfigured' | 'subscribed'.
|
||||
|
||||
%% @type accessModel() = 'open' | 'presence' | 'roster' | 'authorize' | 'whitelist'.
|
||||
|
||||
%% @type pubsubIndex() = {pubsub_index, Index, Last, Free}
|
||||
%% Index = atom()
|
||||
%% Last = integer()
|
||||
%% Free = [integer()].
|
||||
%% internal pubsub index table
|
||||
-record(pubsub_index,
|
||||
{
|
||||
index,
|
||||
last,
|
||||
free
|
||||
}).
|
||||
|
||||
%% @type pubsubNode() = {pubsub_node, NodeId, Id, Parents, Type, Owners, Options}
|
||||
%% NodeId = {host() | ljid(), nodeId()}
|
||||
%% Id = nodeIdx()
|
||||
%% Parents = [nodeId()]
|
||||
%% Type = nodeType()
|
||||
%% Owners = [ljid()]
|
||||
%% Options = [nodeOption()].
|
||||
%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
|
||||
%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
||||
%% <p>The <tt>Parents</tt> and <tt>type</tt> fields are indexed.</p>
|
||||
%% <tt>id</tt> can be anything you want.
|
||||
-record(pubsub_node,
|
||||
{
|
||||
nodeid,
|
||||
id,
|
||||
parents = [],
|
||||
type = "flat",
|
||||
owners = [],
|
||||
options = []
|
||||
}).
|
||||
|
||||
%% @type pubsubState() = {pubsub_state, StateId, NodeIdx, Items, Affiliation, Subscriptions}
|
||||
%% StateId = {ljid(), nodeIdx()}
|
||||
%% NodeIdx = nodeIdx(),
|
||||
%% Items = [itemId()]
|
||||
%% Affiliation = affiliation()
|
||||
%% Subscriptions = [{subscription(), subId()}].
|
||||
%% <p>This is the format of the <tt>affiliations</tt> table. The type of the
|
||||
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
||||
-record(pubsub_state,
|
||||
{
|
||||
stateid,
|
||||
nodeidx,
|
||||
items = [],
|
||||
affiliation = 'none',
|
||||
subscriptions = []
|
||||
}).
|
||||
|
||||
%% @type pubsubItem() = {pubsub_item, ItemId, Creation, Modification, Payload}
|
||||
%% ItemId = {itemId(), nodeIdx()}
|
||||
%% Creation = {now(), ljid()}
|
||||
%% Modification = {now(), ljid()}
|
||||
%% Payload = payload().
|
||||
%% <p>This is the format of the <tt>published items</tt> table. The type of the
|
||||
%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
|
||||
-record(pubsub_item,
|
||||
{
|
||||
itemid,
|
||||
nodeidx,
|
||||
creation = {'unknown','unknown'},
|
||||
modification = {'unknown','unknown'},
|
||||
payload = []
|
||||
}).
|
||||
|
||||
%% @type pubsubSubscription() = {pubsub_subscription, SubId, Options}
|
||||
%% SubId = subId()
|
||||
%% Options = [nodeOption()].
|
||||
%% <p>This is the format of the <tt>subscriptions</tt> table. The type of the
|
||||
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
||||
-record(pubsub_subscription,
|
||||
{
|
||||
subid,
|
||||
options
|
||||
}).
|
||||
|
||||
%% @type pubsubLastItem() = {pubsub_last_item, NodeId, ItemId, Creation, Payload}
|
||||
%% NodeId = nodeIdx()
|
||||
%% ItemId = itemId()
|
||||
%% Creation = {now(),ljid()}
|
||||
%% Payload = payload().
|
||||
%% <p>This is the format of the <tt>last items</tt> table. it stores last item payload
|
||||
%% for every node</p>
|
||||
-record(pubsub_last_item,
|
||||
{
|
||||
nodeid,
|
||||
itemid,
|
||||
creation,
|
||||
payload
|
||||
}).
|
|
@ -0,0 +1,33 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(roster, {usj,
|
||||
us,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none,
|
||||
groups = [],
|
||||
askmessage = [],
|
||||
xs = []}).
|
||||
|
||||
-record(roster_version, {us,
|
||||
version}).
|
|
@ -0,0 +1,34 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(request, {method,
|
||||
path,
|
||||
q = [],
|
||||
us,
|
||||
auth,
|
||||
lang = "",
|
||||
data = "",
|
||||
ip,
|
||||
host, % string()
|
||||
port, % integer()
|
||||
tp, % transfer protocol = http | https
|
||||
headers
|
||||
}).
|
|
@ -0,0 +1,76 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(X(Name), {xmlelement, Name, [], []}).
|
||||
-define(XA(Name, Attrs), {xmlelement, Name, Attrs, []}).
|
||||
-define(XE(Name, Els), {xmlelement, Name, [], Els}).
|
||||
-define(XAE(Name, Attrs, Els), {xmlelement, Name, Attrs, Els}).
|
||||
-define(C(Text), {xmlcdata, Text}).
|
||||
-define(XC(Name, Text), ?XE(Name, [?C(Text)])).
|
||||
-define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])).
|
||||
|
||||
-define(T(Text), translate:translate(Lang, Text)).
|
||||
-define(CT(Text), ?C(?T(Text))).
|
||||
-define(XCT(Name, Text), ?XC(Name, ?T(Text))).
|
||||
-define(XACT(Name, Attrs, Text), ?XAC(Name, Attrs, ?T(Text))).
|
||||
|
||||
-define(LI(Els), ?XE("li", Els)).
|
||||
-define(A(URL, Els), ?XAE("a", [{"href", URL}], Els)).
|
||||
-define(AC(URL, Text), ?A(URL, [?C(Text)])).
|
||||
-define(ACT(URL, Text), ?AC(URL, ?T(Text))).
|
||||
-define(P, ?X("p")).
|
||||
-define(BR, ?X("br")).
|
||||
-define(INPUT(Type, Name, Value),
|
||||
?XA("input", [{"type", Type},
|
||||
{"name", Name},
|
||||
{"value", Value}])).
|
||||
-define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, ?T(Value))).
|
||||
-define(INPUTS(Type, Name, Value, Size),
|
||||
?XA("input", [{"type", Type},
|
||||
{"name", Name},
|
||||
{"value", Value},
|
||||
{"size", Size}])).
|
||||
-define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
|
||||
-define(ACLINPUT(Text), ?XE("td", [?INPUT("text", "value" ++ ID, Text)])).
|
||||
|
||||
-define(TEXTAREA(Name, Rows, Cols, Value),
|
||||
?XAC("textarea", [{"name", Name},
|
||||
{"rows", Rows},
|
||||
{"cols", Cols}],
|
||||
Value)).
|
||||
|
||||
%% Build an xmlelement for result
|
||||
-define(XRES(Text), ?XAC("p", [{"class", "result"}], Text)).
|
||||
-define(XREST(Text), ?XRES(?T(Text))).
|
||||
|
||||
%% Guide Link
|
||||
-define(GL(Ref, Title),
|
||||
?XAE("div",
|
||||
[{"class", "guidelink"}],
|
||||
[?XAE("a",
|
||||
[{"href", "/admin/doc/guide.html#"++ Ref},
|
||||
{"target", "_blank"}],
|
||||
[?C("[Guide: " ++ Title ++ "]")])
|
||||
])).
|
||||
|
||||
|
||||
%% h1 with a Guide Link
|
||||
-define(H1GL(Name, Ref, Title), [?XC("h1", Name), ?GL(Ref, Title)]).
|
|
@ -0,0 +1,263 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : gen_mod.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(gen_mod).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0,
|
||||
start_module/3,
|
||||
stop_module/2,
|
||||
stop_module_keep_config/2,
|
||||
get_opt/2,
|
||||
get_opt/3,
|
||||
get_opt_host/3,
|
||||
db_type/1,
|
||||
db_type/2,
|
||||
get_module_opt/4,
|
||||
get_module_opt_host/3,
|
||||
loaded_modules/1,
|
||||
loaded_modules_with_opts/1,
|
||||
get_hosts/2,
|
||||
get_module_proc/2,
|
||||
is_loaded/2]).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(ejabberd_module, {module_host, opts}).
|
||||
|
||||
behaviour_info(callbacks) ->
|
||||
[{start, 2},
|
||||
{stop, 1}];
|
||||
behaviour_info(_Other) ->
|
||||
undefined.
|
||||
|
||||
start() ->
|
||||
ets:new(ejabberd_modules, [named_table,
|
||||
public,
|
||||
{keypos, #ejabberd_module.module_host}]),
|
||||
ok.
|
||||
|
||||
|
||||
start_module(Host, Module, Opts) ->
|
||||
set_module_opts_mnesia(Host, Module, Opts),
|
||||
ets:insert(ejabberd_modules,
|
||||
#ejabberd_module{module_host = {Module, Host},
|
||||
opts = Opts}),
|
||||
try Module:start(Host, Opts)
|
||||
catch Class:Reason ->
|
||||
del_module_mnesia(Host, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ErrorText = io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p",
|
||||
[Module, Host, Opts, Class, Reason]),
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
case is_app_running(ejabberd) of
|
||||
true ->
|
||||
erlang:raise(Class, Reason, erlang:get_stacktrace());
|
||||
false ->
|
||||
?CRITICAL_MSG("ejabberd initialization was aborted because a module start failed.", []),
|
||||
timer:sleep(3000),
|
||||
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199))
|
||||
end
|
||||
end.
|
||||
|
||||
is_app_running(AppName) ->
|
||||
%% Use a high timeout to prevent a false positive in a high load system
|
||||
Timeout = 15000,
|
||||
lists:keymember(AppName, 1, application:which_applications(Timeout)).
|
||||
|
||||
%% @doc Stop the module in a host, and forget its configuration.
|
||||
stop_module(Host, Module) ->
|
||||
case stop_module_keep_config(Host, Module) of
|
||||
error ->
|
||||
error;
|
||||
ok ->
|
||||
del_module_mnesia(Host, Module)
|
||||
end.
|
||||
|
||||
%% @doc Stop the module in a host, but keep its configuration.
|
||||
%% As the module configuration is kept in the Mnesia local_config table,
|
||||
%% when ejabberd is restarted the module will be started again.
|
||||
%% This function is useful when ejabberd is being stopped
|
||||
%% and it stops all modules.
|
||||
stop_module_keep_config(Host, Module) ->
|
||||
case catch Module:stop(Host) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
error;
|
||||
{wait, ProcList} when is_list(ProcList) ->
|
||||
lists:foreach(fun wait_for_process/1, ProcList),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok;
|
||||
{wait, Process} ->
|
||||
wait_for_process(Process),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok;
|
||||
_ ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok
|
||||
end.
|
||||
|
||||
wait_for_process(Process) ->
|
||||
MonitorReference = erlang:monitor(process, Process),
|
||||
wait_for_stop(Process, MonitorReference).
|
||||
|
||||
wait_for_stop(Process, MonitorReference) ->
|
||||
receive
|
||||
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
|
||||
ok
|
||||
after 5000 ->
|
||||
catch exit(whereis(Process), kill),
|
||||
wait_for_stop1(MonitorReference)
|
||||
end.
|
||||
|
||||
wait_for_stop1(MonitorReference) ->
|
||||
receive
|
||||
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
|
||||
ok
|
||||
after 5000 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
get_opt(Opt, Opts) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
false ->
|
||||
% TODO: replace with more appropriate function
|
||||
throw({undefined_option, Opt});
|
||||
{value, {_, Val}} ->
|
||||
Val
|
||||
end.
|
||||
|
||||
get_opt(Opt, Opts, Default) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
false ->
|
||||
Default;
|
||||
{value, {_, Val}} ->
|
||||
Val
|
||||
end.
|
||||
|
||||
get_module_opt(global, Module, Opt, Default) ->
|
||||
Hosts = ?MYHOSTS,
|
||||
[Value | Values] = lists:map(
|
||||
fun(Host) ->
|
||||
get_module_opt(Host, Module, Opt, Default)
|
||||
end,
|
||||
Hosts),
|
||||
Same_all = lists:all(
|
||||
fun(Other_value) ->
|
||||
Other_value == Value
|
||||
end,
|
||||
Values),
|
||||
case Same_all of
|
||||
true -> Value;
|
||||
false -> Default
|
||||
end;
|
||||
|
||||
get_module_opt(Host, Module, Opt, Default) ->
|
||||
OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
|
||||
case OptsList of
|
||||
[] ->
|
||||
Default;
|
||||
[#ejabberd_module{opts = Opts} | _] ->
|
||||
get_opt(Opt, Opts, Default)
|
||||
end.
|
||||
|
||||
get_module_opt_host(Host, Module, Default) ->
|
||||
Val = get_module_opt(Host, Module, host, Default),
|
||||
ejabberd_regexp:greplace(Val, "@HOST@", Host).
|
||||
|
||||
get_opt_host(Host, Opts, Default) ->
|
||||
Val = get_opt(host, Opts, Default),
|
||||
ejabberd_regexp:greplace(Val, "@HOST@", Host).
|
||||
|
||||
db_type(Opts) ->
|
||||
case get_opt(db_type, Opts, mnesia) of
|
||||
odbc -> odbc;
|
||||
_ -> mnesia
|
||||
end.
|
||||
|
||||
db_type(Host, Module) ->
|
||||
case get_module_opt(Host, Module, db_type, mnesia) of
|
||||
odbc -> odbc;
|
||||
_ -> mnesia
|
||||
end.
|
||||
|
||||
loaded_modules(Host) ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
|
||||
[],
|
||||
['$1']}]).
|
||||
|
||||
loaded_modules_with_opts(Host) ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host},
|
||||
opts = '$2'},
|
||||
[],
|
||||
[{{'$1', '$2'}}]}]).
|
||||
|
||||
set_module_opts_mnesia(Host, Module, Opts) ->
|
||||
Modules = case ejabberd_config:get_local_option({modules, Host}) of
|
||||
undefined ->
|
||||
[];
|
||||
Ls ->
|
||||
Ls
|
||||
end,
|
||||
Modules1 = lists:keydelete(Module, 1, Modules),
|
||||
Modules2 = [{Module, Opts} | Modules1],
|
||||
ejabberd_config:add_local_option({modules, Host}, Modules2).
|
||||
|
||||
del_module_mnesia(Host, Module) ->
|
||||
Modules = case ejabberd_config:get_local_option({modules, Host}) of
|
||||
undefined ->
|
||||
[];
|
||||
Ls ->
|
||||
Ls
|
||||
end,
|
||||
Modules1 = lists:keydelete(Module, 1, Modules),
|
||||
ejabberd_config:add_local_option({modules, Host}, Modules1).
|
||||
|
||||
get_hosts(Opts, Prefix) ->
|
||||
case catch gen_mod:get_opt(hosts, Opts) of
|
||||
{'EXIT', _Error1} ->
|
||||
case catch gen_mod:get_opt(host, Opts) of
|
||||
{'EXIT', _Error2} ->
|
||||
[Prefix ++ Host || Host <- ?MYHOSTS];
|
||||
Host ->
|
||||
[Host]
|
||||
end;
|
||||
Hosts ->
|
||||
Hosts
|
||||
end.
|
||||
|
||||
get_module_proc(Host, {frontend, Base}) ->
|
||||
get_module_proc("frontend_" ++ Host, Base);
|
||||
get_module_proc(Host, Base) ->
|
||||
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
|
||||
|
||||
is_loaded(Host, Module) ->
|
||||
ets:member(ejabberd_modules, {Module, Host}).
|
||||
|
Binary file not shown.
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
IBClasses = (
|
||||
{
|
||||
CLASS = NSPreferencePane;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
"_firstKeyView" = id;
|
||||
"_initialKeyView" = id;
|
||||
"_lastKeyView" = id;
|
||||
"_window" = id;
|
||||
};
|
||||
SUPERCLASS = NSObject;
|
||||
},
|
||||
{
|
||||
ACTIONS = {automaticStartAction = id; startStopAction = id; };
|
||||
CLASS = ejabberdController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
actionProgress = NSProgressIndicator;
|
||||
automaticBox = NSButton;
|
||||
imagestarted = NSImageView;
|
||||
imagestopped = NSImageView;
|
||||
startStopButton = NSButton;
|
||||
status = NSTextField;
|
||||
};
|
||||
SUPERCLASS = NSObject;
|
||||
}
|
||||
);
|
||||
IBVersion = 1;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>32 22 356 240 0 0 1680 1028 </string>
|
||||
<key>IBFramework Version</key>
|
||||
<string>446.1</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>2</integer>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8S2167</string>
|
||||
</dict>
|
||||
</plist>
|
Binary file not shown.
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>ejabberd</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.prefpanel</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>process-one</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0.3</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>ejabberdPref</string>
|
||||
<key>NSPrefPaneIconFile</key>
|
||||
<string>ejabberdPref.tiff</string>
|
||||
<key>NSPrefPaneIconLabel</key>
|
||||
<string>ejabberd</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>ejabberdPref</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,18 @@
|
|||
ejabberd's prefPane Licence
|
||||
|
||||
Except as described below, all of the source code to prefPane is
|
||||
available under the Mozilla Public Licence (MPL).
|
||||
|
||||
The complete text of the Mozilla Public License can be found in
|
||||
the MPL file in the same directory as this file or consulted at
|
||||
http://www.mozilla.org/MPL/MPL-1.1.html.
|
||||
|
||||
Exceptions
|
||||
|
||||
The following portions are not available under the above
|
||||
terms:
|
||||
|
||||
* Image files containing the trademarks and logos of
|
||||
ProcessOne, which may not be reproduced without
|
||||
permission. (Copyright ©2001-2009 ProcessOne. All Rights
|
||||
Reserved.)
|
|
@ -0,0 +1,470 @@
|
|||
MOZILLA PUBLIC LICENSE
|
||||
Version 1.1
|
||||
|
||||
---------------
|
||||
|
||||
1. Definitions.
|
||||
|
||||
1.0.1. "Commercial Use" means distribution or otherwise making the
|
||||
Covered Code available to a third party.
|
||||
|
||||
1.1. "Contributor" means each entity that creates or contributes to
|
||||
the creation of Modifications.
|
||||
|
||||
1.2. "Contributor Version" means the combination of the Original
|
||||
Code, prior Modifications used by a Contributor, and the Modifications
|
||||
made by that particular Contributor.
|
||||
|
||||
1.3. "Covered Code" means the Original Code or Modifications or the
|
||||
combination of the Original Code and Modifications, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.4. "Electronic Distribution Mechanism" means a mechanism generally
|
||||
accepted in the software development community for the electronic
|
||||
transfer of data.
|
||||
|
||||
1.5. "Executable" means Covered Code in any form other than Source
|
||||
Code.
|
||||
|
||||
1.6. "Initial Developer" means the individual or entity identified
|
||||
as the Initial Developer in the Source Code notice required by Exhibit
|
||||
A.
|
||||
|
||||
1.7. "Larger Work" means a work which combines Covered Code or
|
||||
portions thereof with code not governed by the terms of this License.
|
||||
|
||||
1.8. "License" means this document.
|
||||
|
||||
1.8.1. "Licensable" means having the right to grant, to the maximum
|
||||
extent possible, whether at the time of the initial grant or
|
||||
subsequently acquired, any and all of the rights conveyed herein.
|
||||
|
||||
1.9. "Modifications" means any addition to or deletion from the
|
||||
substance or structure of either the Original Code or any previous
|
||||
Modifications. When Covered Code is released as a series of files, a
|
||||
Modification is:
|
||||
A. Any addition to or deletion from the contents of a file
|
||||
containing Original Code or previous Modifications.
|
||||
|
||||
B. Any new file that contains any part of the Original Code or
|
||||
previous Modifications.
|
||||
|
||||
1.10. "Original Code" means Source Code of computer software code
|
||||
which is described in the Source Code notice required by Exhibit A as
|
||||
Original Code, and which, at the time of its release under this
|
||||
License is not already Covered Code governed by this License.
|
||||
|
||||
1.10.1. "Patent Claims" means any patent claim(s), now owned or
|
||||
hereafter acquired, including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by grantor.
|
||||
|
||||
1.11. "Source Code" means the preferred form of the Covered Code for
|
||||
making modifications to it, including all modules it contains, plus
|
||||
any associated interface definition files, scripts used to control
|
||||
compilation and installation of an Executable, or source code
|
||||
differential comparisons against either the Original Code or another
|
||||
well known, available Covered Code of the Contributor's choice. The
|
||||
Source Code can be in a compressed or archival form, provided the
|
||||
appropriate decompression or de-archiving software is widely available
|
||||
for no charge.
|
||||
|
||||
1.12. "You" (or "Your") means an individual or a legal entity
|
||||
exercising rights under, and complying with all of the terms of, this
|
||||
License or a future version of this License issued under Section 6.1.
|
||||
For legal entities, "You" includes any entity which controls, is
|
||||
controlled by, or is under common control with You. For purposes of
|
||||
this definition, "control" means (a) the power, direct or indirect,
|
||||
to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (b) ownership of more than fifty percent
|
||||
(50%) of the outstanding shares or beneficial ownership of such
|
||||
entity.
|
||||
|
||||
2. Source Code License.
|
||||
|
||||
2.1. The Initial Developer Grant.
|
||||
The Initial Developer hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license, subject to third party intellectual property
|
||||
claims:
|
||||
(a) under intellectual property rights (other than patent or
|
||||
trademark) Licensable by Initial Developer to use, reproduce,
|
||||
modify, display, perform, sublicense and distribute the Original
|
||||
Code (or portions thereof) with or without Modifications, and/or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patents Claims infringed by the making, using or
|
||||
selling of Original Code, to make, have made, use, practice,
|
||||
sell, and offer for sale, and/or otherwise dispose of the
|
||||
Original Code (or portions thereof).
|
||||
|
||||
(c) the licenses granted in this Section 2.1(a) and (b) are
|
||||
effective on the date Initial Developer first distributes
|
||||
Original Code under the terms of this License.
|
||||
|
||||
(d) Notwithstanding Section 2.1(b) above, no patent license is
|
||||
granted: 1) for code that You delete from the Original Code; 2)
|
||||
separate from the Original Code; or 3) for infringements caused
|
||||
by: i) the modification of the Original Code or ii) the
|
||||
combination of the Original Code with other software or devices.
|
||||
|
||||
2.2. Contributor Grant.
|
||||
Subject to third party intellectual property claims, each Contributor
|
||||
hereby grants You a world-wide, royalty-free, non-exclusive license
|
||||
|
||||
(a) under intellectual property rights (other than patent or
|
||||
trademark) Licensable by Contributor, to use, reproduce, modify,
|
||||
display, perform, sublicense and distribute the Modifications
|
||||
created by such Contributor (or portions thereof) either on an
|
||||
unmodified basis, with other Modifications, as Covered Code
|
||||
and/or as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims infringed by the making, using, or
|
||||
selling of Modifications made by that Contributor either alone
|
||||
and/or in combination with its Contributor Version (or portions
|
||||
of such combination), to make, use, sell, offer for sale, have
|
||||
made, and/or otherwise dispose of: 1) Modifications made by that
|
||||
Contributor (or portions thereof); and 2) the combination of
|
||||
Modifications made by that Contributor with its Contributor
|
||||
Version (or portions of such combination).
|
||||
|
||||
(c) the licenses granted in Sections 2.2(a) and 2.2(b) are
|
||||
effective on the date Contributor first makes Commercial Use of
|
||||
the Covered Code.
|
||||
|
||||
(d) Notwithstanding Section 2.2(b) above, no patent license is
|
||||
granted: 1) for any code that Contributor has deleted from the
|
||||
Contributor Version; 2) separate from the Contributor Version;
|
||||
3) for infringements caused by: i) third party modifications of
|
||||
Contributor Version or ii) the combination of Modifications made
|
||||
by that Contributor with other software (except as part of the
|
||||
Contributor Version) or other devices; or 4) under Patent Claims
|
||||
infringed by Covered Code in the absence of Modifications made by
|
||||
that Contributor.
|
||||
|
||||
3. Distribution Obligations.
|
||||
|
||||
3.1. Application of License.
|
||||
The Modifications which You create or to which You contribute are
|
||||
governed by the terms of this License, including without limitation
|
||||
Section 2.2. The Source Code version of Covered Code may be
|
||||
distributed only under the terms of this License or a future version
|
||||
of this License released under Section 6.1, and You must include a
|
||||
copy of this License with every copy of the Source Code You
|
||||
distribute. You may not offer or impose any terms on any Source Code
|
||||
version that alters or restricts the applicable version of this
|
||||
License or the recipients' rights hereunder. However, You may include
|
||||
an additional document offering the additional rights described in
|
||||
Section 3.5.
|
||||
|
||||
3.2. Availability of Source Code.
|
||||
Any Modification which You create or to which You contribute must be
|
||||
made available in Source Code form under the terms of this License
|
||||
either on the same media as an Executable version or via an accepted
|
||||
Electronic Distribution Mechanism to anyone to whom you made an
|
||||
Executable version available; and if made available via Electronic
|
||||
Distribution Mechanism, must remain available for at least twelve (12)
|
||||
months after the date it initially became available, or at least six
|
||||
(6) months after a subsequent version of that particular Modification
|
||||
has been made available to such recipients. You are responsible for
|
||||
ensuring that the Source Code version remains available even if the
|
||||
Electronic Distribution Mechanism is maintained by a third party.
|
||||
|
||||
3.3. Description of Modifications.
|
||||
You must cause all Covered Code to which You contribute to contain a
|
||||
file documenting the changes You made to create that Covered Code and
|
||||
the date of any change. You must include a prominent statement that
|
||||
the Modification is derived, directly or indirectly, from Original
|
||||
Code provided by the Initial Developer and including the name of the
|
||||
Initial Developer in (a) the Source Code, and (b) in any notice in an
|
||||
Executable version or related documentation in which You describe the
|
||||
origin or ownership of the Covered Code.
|
||||
|
||||
3.4. Intellectual Property Matters
|
||||
(a) Third Party Claims.
|
||||
If Contributor has knowledge that a license under a third party's
|
||||
intellectual property rights is required to exercise the rights
|
||||
granted by such Contributor under Sections 2.1 or 2.2,
|
||||
Contributor must include a text file with the Source Code
|
||||
distribution titled "LEGAL" which describes the claim and the
|
||||
party making the claim in sufficient detail that a recipient will
|
||||
know whom to contact. If Contributor obtains such knowledge after
|
||||
the Modification is made available as described in Section 3.2,
|
||||
Contributor shall promptly modify the LEGAL file in all copies
|
||||
Contributor makes available thereafter and shall take other steps
|
||||
(such as notifying appropriate mailing lists or newsgroups)
|
||||
reasonably calculated to inform those who received the Covered
|
||||
Code that new knowledge has been obtained.
|
||||
|
||||
(b) Contributor APIs.
|
||||
If Contributor's Modifications include an application programming
|
||||
interface and Contributor has knowledge of patent licenses which
|
||||
are reasonably necessary to implement that API, Contributor must
|
||||
also include this information in the LEGAL file.
|
||||
|
||||
(c) Representations.
|
||||
Contributor represents that, except as disclosed pursuant to
|
||||
Section 3.4(a) above, Contributor believes that Contributor's
|
||||
Modifications are Contributor's original creation(s) and/or
|
||||
Contributor has sufficient rights to grant the rights conveyed by
|
||||
this License.
|
||||
|
||||
3.5. Required Notices.
|
||||
You must duplicate the notice in Exhibit A in each file of the Source
|
||||
Code. If it is not possible to put such notice in a particular Source
|
||||
Code file due to its structure, then You must include such notice in a
|
||||
location (such as a relevant directory) where a user would be likely
|
||||
to look for such a notice. If You created one or more Modification(s)
|
||||
You may add your name as a Contributor to the notice described in
|
||||
Exhibit A. You must also duplicate this License in any documentation
|
||||
for the Source Code where You describe recipients' rights or ownership
|
||||
rights relating to Covered Code. You may choose to offer, and to
|
||||
charge a fee for, warranty, support, indemnity or liability
|
||||
obligations to one or more recipients of Covered Code. However, You
|
||||
may do so only on Your own behalf, and not on behalf of the Initial
|
||||
Developer or any Contributor. You must make it absolutely clear than
|
||||
any such warranty, support, indemnity or liability obligation is
|
||||
offered by You alone, and You hereby agree to indemnify the Initial
|
||||
Developer and every Contributor for any liability incurred by the
|
||||
Initial Developer or such Contributor as a result of warranty,
|
||||
support, indemnity or liability terms You offer.
|
||||
|
||||
3.6. Distribution of Executable Versions.
|
||||
You may distribute Covered Code in Executable form only if the
|
||||
requirements of Section 3.1-3.5 have been met for that Covered Code,
|
||||
and if You include a notice stating that the Source Code version of
|
||||
the Covered Code is available under the terms of this License,
|
||||
including a description of how and where You have fulfilled the
|
||||
obligations of Section 3.2. The notice must be conspicuously included
|
||||
in any notice in an Executable version, related documentation or
|
||||
collateral in which You describe recipients' rights relating to the
|
||||
Covered Code. You may distribute the Executable version of Covered
|
||||
Code or ownership rights under a license of Your choice, which may
|
||||
contain terms different from this License, provided that You are in
|
||||
compliance with the terms of this License and that the license for the
|
||||
Executable version does not attempt to limit or alter the recipient's
|
||||
rights in the Source Code version from the rights set forth in this
|
||||
License. If You distribute the Executable version under a different
|
||||
license You must make it absolutely clear that any terms which differ
|
||||
from this License are offered by You alone, not by the Initial
|
||||
Developer or any Contributor. You hereby agree to indemnify the
|
||||
Initial Developer and every Contributor for any liability incurred by
|
||||
the Initial Developer or such Contributor as a result of any such
|
||||
terms You offer.
|
||||
|
||||
3.7. Larger Works.
|
||||
You may create a Larger Work by combining Covered Code with other code
|
||||
not governed by the terms of this License and distribute the Larger
|
||||
Work as a single product. In such a case, You must make sure the
|
||||
requirements of this License are fulfilled for the Covered Code.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation.
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Code due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description
|
||||
must be included in the LEGAL file described in Section 3.4 and must
|
||||
be included with all distributions of the Source Code. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Application of this License.
|
||||
|
||||
This License applies to code to which the Initial Developer has
|
||||
attached the notice in Exhibit A and to related Covered Code.
|
||||
|
||||
6. Versions of the License.
|
||||
|
||||
6.1. New Versions.
|
||||
Netscape Communications Corporation ("Netscape") may publish revised
|
||||
and/or new versions of the License from time to time. Each version
|
||||
will be given a distinguishing version number.
|
||||
|
||||
6.2. Effect of New Versions.
|
||||
Once Covered Code has been published under a particular version of the
|
||||
License, You may always continue to use it under the terms of that
|
||||
version. You may also choose to use such Covered Code under the terms
|
||||
of any subsequent version of the License published by Netscape. No one
|
||||
other than Netscape has the right to modify the terms applicable to
|
||||
Covered Code created under this License.
|
||||
|
||||
6.3. Derivative Works.
|
||||
If You create or use a modified version of this License (which you may
|
||||
only do in order to apply it to code which is not already Covered Code
|
||||
governed by this License), You must (a) rename Your license so that
|
||||
the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
|
||||
"MPL", "NPL" or any confusingly similar phrase do not appear in your
|
||||
license (except to note that your license differs from this License)
|
||||
and (b) otherwise make it clear that Your version of the license
|
||||
contains terms which differ from the Mozilla Public License and
|
||||
Netscape Public License. (Filling in the name of the Initial
|
||||
Developer, Original Code or Contributor in the notice described in
|
||||
Exhibit A shall not of themselves be deemed to be modifications of
|
||||
this License.)
|
||||
|
||||
7. DISCLAIMER OF WARRANTY.
|
||||
|
||||
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
|
||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||
WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
|
||||
DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
|
||||
THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
|
||||
IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
|
||||
YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
|
||||
COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
|
||||
OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
|
||||
ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
|
||||
|
||||
8. TERMINATION.
|
||||
|
||||
8.1. This License and the rights granted hereunder will terminate
|
||||
automatically if You fail to comply with terms herein and fail to cure
|
||||
such breach within 30 days of becoming aware of the breach. All
|
||||
sublicenses to the Covered Code which are properly granted shall
|
||||
survive any termination of this License. Provisions which, by their
|
||||
nature, must remain in effect beyond the termination of this License
|
||||
shall survive.
|
||||
|
||||
8.2. If You initiate litigation by asserting a patent infringement
|
||||
claim (excluding declatory judgment actions) against Initial Developer
|
||||
or a Contributor (the Initial Developer or Contributor against whom
|
||||
You file such action is referred to as "Participant") alleging that:
|
||||
|
||||
(a) such Participant's Contributor Version directly or indirectly
|
||||
infringes any patent, then any and all rights granted by such
|
||||
Participant to You under Sections 2.1 and/or 2.2 of this License
|
||||
shall, upon 60 days notice from Participant terminate prospectively,
|
||||
unless if within 60 days after receipt of notice You either: (i)
|
||||
agree in writing to pay Participant a mutually agreeable reasonable
|
||||
royalty for Your past and future use of Modifications made by such
|
||||
Participant, or (ii) withdraw Your litigation claim with respect to
|
||||
the Contributor Version against such Participant. If within 60 days
|
||||
of notice, a reasonable royalty and payment arrangement are not
|
||||
mutually agreed upon in writing by the parties or the litigation claim
|
||||
is not withdrawn, the rights granted by Participant to You under
|
||||
Sections 2.1 and/or 2.2 automatically terminate at the expiration of
|
||||
the 60 day notice period specified above.
|
||||
|
||||
(b) any software, hardware, or device, other than such Participant's
|
||||
Contributor Version, directly or indirectly infringes any patent, then
|
||||
any rights granted to You by such Participant under Sections 2.1(b)
|
||||
and 2.2(b) are revoked effective as of the date You first made, used,
|
||||
sold, distributed, or had made, Modifications made by that
|
||||
Participant.
|
||||
|
||||
8.3. If You assert a patent infringement claim against Participant
|
||||
alleging that such Participant's Contributor Version directly or
|
||||
indirectly infringes any patent where such claim is resolved (such as
|
||||
by license or settlement) prior to the initiation of patent
|
||||
infringement litigation, then the reasonable value of the licenses
|
||||
granted by such Participant under Sections 2.1 or 2.2 shall be taken
|
||||
into account in determining the amount or value of any payment or
|
||||
license.
|
||||
|
||||
8.4. In the event of termination under Sections 8.1 or 8.2 above,
|
||||
all end user license agreements (excluding distributors and resellers)
|
||||
which have been validly granted by You or any distributor hereunder
|
||||
prior to termination shall survive termination.
|
||||
|
||||
9. LIMITATION OF LIABILITY.
|
||||
|
||||
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
|
||||
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
|
||||
DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
|
||||
OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
|
||||
ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
|
||||
CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
|
||||
WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
|
||||
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
|
||||
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
|
||||
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
|
||||
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
|
||||
PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
|
||||
EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
|
||||
THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
10. U.S. GOVERNMENT END USERS.
|
||||
|
||||
The Covered Code is a "commercial item," as that term is defined in
|
||||
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
|
||||
software" and "commercial computer software documentation," as such
|
||||
terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
|
||||
C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
|
||||
all U.S. Government End Users acquire Covered Code with only those
|
||||
rights set forth herein.
|
||||
|
||||
11. MISCELLANEOUS.
|
||||
|
||||
This License represents the complete agreement concerning subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. This License shall be governed by
|
||||
California law provisions (except to the extent applicable law, if
|
||||
any, provides otherwise), excluding its conflict-of-law provisions.
|
||||
With respect to disputes in which at least one party is a citizen of,
|
||||
or an entity chartered or registered to do business in the United
|
||||
States of America, any litigation relating to this License shall be
|
||||
subject to the jurisdiction of the Federal Courts of the Northern
|
||||
District of California, with venue lying in Santa Clara County,
|
||||
California, with the losing party responsible for costs, including
|
||||
without limitation, court costs and reasonable attorneys' fees and
|
||||
expenses. The application of the United Nations Convention on
|
||||
Contracts for the International Sale of Goods is expressly excluded.
|
||||
Any law or regulation which provides that the language of a contract
|
||||
shall be construed against the drafter shall not apply to this
|
||||
License.
|
||||
|
||||
12. RESPONSIBILITY FOR CLAIMS.
|
||||
|
||||
As between Initial Developer and the Contributors, each party is
|
||||
responsible for claims and damages arising, directly or indirectly,
|
||||
out of its utilization of rights under this License and You agree to
|
||||
work with Initial Developer and Contributors to distribute such
|
||||
responsibility on an equitable basis. Nothing herein is intended or
|
||||
shall be deemed to constitute any admission of liability.
|
||||
|
||||
13. MULTIPLE-LICENSED CODE.
|
||||
|
||||
Initial Developer may designate portions of the Covered Code as
|
||||
"Multiple-Licensed". "Multiple-Licensed" means that the Initial
|
||||
Developer permits you to utilize portions of the Covered Code under
|
||||
Your choice of the NPL or the alternative licenses, if any, specified
|
||||
by the Initial Developer in the file described in Exhibit A.
|
||||
|
||||
EXHIBIT A -Mozilla Public License.
|
||||
|
||||
``The contents of this file are subject to the Mozilla Public License
|
||||
Version 1.1 (the "License"); you may not use this file except in
|
||||
compliance with the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS"
|
||||
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing rights and limitations
|
||||
under the License.
|
||||
|
||||
The Original Code is ______________________________________.
|
||||
|
||||
The Initial Developer of the Original Code is ________________________.
|
||||
Portions created by ______________________ are Copyright (C) ______
|
||||
_______________________. All Rights Reserved.
|
||||
|
||||
Contributor(s): ______________________________________.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms
|
||||
of the _____ license (the "[___] License"), in which case the
|
||||
provisions of [______] License are applicable instead of those
|
||||
above. If you wish to allow use of your version of this file only
|
||||
under the terms of the [____] License and not to allow others to use
|
||||
your version of this file under the MPL, indicate your decision by
|
||||
deleting the provisions above and replace them with the notice and
|
||||
other provisions required by the [___] License. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file
|
||||
under either the MPL or the [___] License."
|
||||
|
||||
[NOTE: The text of this Exhibit A may differ slightly from the text of
|
||||
the notices in the Source Code files of the Original Code. You should
|
||||
use the text of this Exhibit A rather than the text found in the
|
||||
Original Code Source Code for Your Modifications.]
|
||||
|
|
@ -0,0 +1 @@
|
|||
/Applications/ejabberd-2.0.3
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>ejabberd.etc.rc.command</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Applications/ejabberd-2.0.3/bin/ejabberdctl</string>
|
||||
<string>start</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,439 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 42;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
837D1CC80B635DA9003F99C1 /* ejabberd.plist in Resources */ = {isa = PBXBuildFile; fileRef = 837D1CC70B635DA9003F99C1 /* ejabberd.plist */; };
|
||||
837F6DD209F777FF00914317 /* ejabberdController.h in Headers */ = {isa = PBXBuildFile; fileRef = 837F6DD009F777FF00914317 /* ejabberdController.h */; };
|
||||
837F6DD309F777FF00914317 /* ejabberdController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837F6DD109F777FF00914317 /* ejabberdController.m */; };
|
||||
837F6DDD09F7E55000914317 /* logo_ejabberd.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DDB09F7E55000914317 /* logo_ejabberd.png */; };
|
||||
837F6DDF09F7E6FD00914317 /* logo_ejabberd.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DDE09F7E6FD00914317 /* logo_ejabberd.png */; };
|
||||
837F6DE409F7E8BF00914317 /* instance_started.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DE209F7E8BF00914317 /* instance_started.png */; };
|
||||
837F6DE509F7E8BF00914317 /* instance_stopped.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DE309F7E8BF00914317 /* instance_stopped.png */; };
|
||||
837F6DEE09F8D41200914317 /* config.txt in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DED09F8D41200914317 /* config.txt */; };
|
||||
8D202CEA0486D31800D8A456 /* ejabberd_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 32DBCFA20370C41700C91783 /* ejabberd_Prefix.pch */; };
|
||||
8D202CEB0486D31800D8A456 /* ejabberdPref.h in Headers */ = {isa = PBXBuildFile; fileRef = F506C03C013D9D7901CA16C8 /* ejabberdPref.h */; };
|
||||
8D202CED0486D31800D8A456 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; };
|
||||
8D202CEE0486D31800D8A456 /* ejabberdPref.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F506C040013D9D8001CA16C8 /* ejabberdPref.tiff */; };
|
||||
8D202CEF0486D31800D8A456 /* ejabberdPref.nib in Resources */ = {isa = PBXBuildFile; fileRef = F506C042013D9D8C01CA16C8 /* ejabberdPref.nib */; };
|
||||
8D202CF10486D31800D8A456 /* ejabberdPref.m in Sources */ = {isa = PBXBuildFile; fileRef = F506C03D013D9D7901CA16C8 /* ejabberdPref.m */; };
|
||||
8D202CF30486D31800D8A456 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
|
||||
8D202CF40486D31800D8A456 /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F506C035013D953901CA16C8 /* PreferencePanes.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
32DBCFA20370C41700C91783 /* ejabberd_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ejabberd_Prefix.pch; sourceTree = "<group>"; };
|
||||
837D1CC70B635DA9003F99C1 /* ejabberd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = ejabberd.plist; sourceTree = "<group>"; };
|
||||
837F6DD009F777FF00914317 /* ejabberdController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ejabberdController.h; sourceTree = "<group>"; };
|
||||
837F6DD109F777FF00914317 /* ejabberdController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ejabberdController.m; sourceTree = "<group>"; };
|
||||
837F6DDB09F7E55000914317 /* logo_ejabberd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_ejabberd.png; sourceTree = "<group>"; };
|
||||
837F6DDE09F7E6FD00914317 /* logo_ejabberd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_ejabberd.png; sourceTree = "<group>"; };
|
||||
837F6DE209F7E8BF00914317 /* instance_started.png */ = {isa = PBXFileReference; explicitFileType = image.png; path = instance_started.png; sourceTree = "<group>"; };
|
||||
837F6DE309F7E8BF00914317 /* instance_stopped.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = instance_stopped.png; sourceTree = "<group>"; };
|
||||
837F6DED09F8D41200914317 /* config.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = config.txt; sourceTree = "<group>"; };
|
||||
8D202CF70486D31800D8A456 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D202CF80486D31800D8A456 /* ejabberd.prefPane */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ejabberd.prefPane; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F506C035013D953901CA16C8 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = "<absolute>"; };
|
||||
F506C03C013D9D7901CA16C8 /* ejabberdPref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ejabberdPref.h; sourceTree = "<group>"; };
|
||||
F506C03D013D9D7901CA16C8 /* ejabberdPref.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ejabberdPref.m; sourceTree = "<group>"; };
|
||||
F506C040013D9D8001CA16C8 /* ejabberdPref.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = ejabberdPref.tiff; sourceTree = "<group>"; };
|
||||
F506C043013D9D8C01CA16C8 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/ejabberdPref.nib; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8D202CF20486D31800D8A456 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D202CF30486D31800D8A456 /* Cocoa.framework in Frameworks */,
|
||||
8D202CF40486D31800D8A456 /* PreferencePanes.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
089C166AFE841209C02AAC07 /* ejabberd */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB77AFFE84173DC02AAC07 /* Classes */,
|
||||
32DBCFA10370C40200C91783 /* Other Sources */,
|
||||
089C167CFE841241C02AAC07 /* Resources */,
|
||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
|
||||
19C28FB8FE9D52D311CA2CBB /* Products */,
|
||||
);
|
||||
name = ejabberd;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */,
|
||||
1058C7AEFEA557BF11CA2CBB /* Other Frameworks */,
|
||||
);
|
||||
name = "Frameworks and Libraries";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
089C167CFE841241C02AAC07 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
837F6DE209F7E8BF00914317 /* instance_started.png */,
|
||||
837F6DE309F7E8BF00914317 /* instance_stopped.png */,
|
||||
8D202CF70486D31800D8A456 /* Info.plist */,
|
||||
089C167DFE841241C02AAC07 /* InfoPlist.strings */,
|
||||
F506C040013D9D8001CA16C8 /* ejabberdPref.tiff */,
|
||||
F506C042013D9D8C01CA16C8 /* ejabberdPref.nib */,
|
||||
837F6DDB09F7E55000914317 /* logo_ejabberd.png */,
|
||||
837F6DDE09F7E6FD00914317 /* logo_ejabberd.png */,
|
||||
837F6DED09F8D41200914317 /* config.txt */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB77AFFE84173DC02AAC07 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F506C03C013D9D7901CA16C8 /* ejabberdPref.h */,
|
||||
F506C03D013D9D7901CA16C8 /* ejabberdPref.m */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */,
|
||||
F506C035013D953901CA16C8 /* PreferencePanes.framework */,
|
||||
);
|
||||
name = "Linked Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
089C1672FE841209C02AAC07 /* Foundation.framework */,
|
||||
089C167FFE841241C02AAC07 /* AppKit.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
19C28FB8FE9D52D311CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8D202CF80486D31800D8A456 /* ejabberd.prefPane */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32DBCFA10370C40200C91783 /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
837D1CC70B635DA9003F99C1 /* ejabberd.plist */,
|
||||
837F6DD009F777FF00914317 /* ejabberdController.h */,
|
||||
837F6DD109F777FF00914317 /* ejabberdController.m */,
|
||||
32DBCFA20370C41700C91783 /* ejabberd_Prefix.pch */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8D202CE90486D31800D8A456 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D202CEA0486D31800D8A456 /* ejabberd_Prefix.pch in Headers */,
|
||||
8D202CEB0486D31800D8A456 /* ejabberdPref.h in Headers */,
|
||||
837F6DD209F777FF00914317 /* ejabberdController.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8D202CE80486D31800D8A456 /* ejabberd */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 83231E620B57EBEA006434E8 /* Build configuration list for PBXNativeTarget "ejabberd" */;
|
||||
buildPhases = (
|
||||
8D202CE90486D31800D8A456 /* Headers */,
|
||||
8D202CEC0486D31800D8A456 /* Resources */,
|
||||
8D202CF00486D31800D8A456 /* Sources */,
|
||||
8D202CF20486D31800D8A456 /* Frameworks */,
|
||||
8D202CF50486D31800D8A456 /* Rez */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ejabberd;
|
||||
productInstallPath = "$(HOME)/Library/PreferencePanes";
|
||||
productName = ejabberd;
|
||||
productReference = 8D202CF80486D31800D8A456 /* ejabberd.prefPane */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
089C1669FE841209C02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 83231E660B57EBEA006434E8 /* Build configuration list for PBXProject "ejabberd" */;
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 089C166AFE841209C02AAC07 /* ejabberd */;
|
||||
projectDirPath = "";
|
||||
targets = (
|
||||
8D202CE80486D31800D8A456 /* ejabberd */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8D202CEC0486D31800D8A456 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D202CED0486D31800D8A456 /* InfoPlist.strings in Resources */,
|
||||
8D202CEE0486D31800D8A456 /* ejabberdPref.tiff in Resources */,
|
||||
8D202CEF0486D31800D8A456 /* ejabberdPref.nib in Resources */,
|
||||
837F6DDD09F7E55000914317 /* logo_ejabberd.png in Resources */,
|
||||
837F6DDF09F7E6FD00914317 /* logo_ejabberd.png in Resources */,
|
||||
837F6DE409F7E8BF00914317 /* instance_started.png in Resources */,
|
||||
837F6DE509F7E8BF00914317 /* instance_stopped.png in Resources */,
|
||||
837F6DEE09F8D41200914317 /* config.txt in Resources */,
|
||||
837D1CC80B635DA9003F99C1 /* ejabberd.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXRezBuildPhase section */
|
||||
8D202CF50486D31800D8A456 /* Rez */ = {
|
||||
isa = PBXRezBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXRezBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8D202CF00486D31800D8A456 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D202CF10486D31800D8A456 /* ejabberdPref.m in Sources */,
|
||||
837F6DD309F777FF00914317 /* ejabberdController.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
089C167DFE841241C02AAC07 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
089C167EFE841241C02AAC07 /* English */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F506C042013D9D8C01CA16C8 /* ejabberdPref.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F506C043013D9D8C01CA16C8 /* English */,
|
||||
);
|
||||
name = ejabberdPref.nib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
83231E630B57EBEA006434E8 /* Development */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUGGING_SYMBOLS = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_ENABLE_TRIGRAPHS = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = ejabberd_Prefix.pch;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = NO;
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Library/PreferencePanes";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
LIBRARY_STYLE = Bundle;
|
||||
OPTIMIZATION_CFLAGS = "-O0";
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = (
|
||||
"-bundle",
|
||||
"-twolevel_namespace",
|
||||
);
|
||||
OTHER_REZFLAGS = "";
|
||||
PRODUCT_NAME = ejabberd;
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wmost",
|
||||
"-Wno-four-char-constants",
|
||||
"-Wno-unknown-pragmas",
|
||||
);
|
||||
WRAPPER_EXTENSION = prefPane;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Development;
|
||||
};
|
||||
83231E640B57EBEA006434E8 /* Deployment */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
COPY_PHASE_STRIP = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_ENABLE_TRIGRAPHS = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = ejabberd_Prefix.pch;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = NO;
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Library/PreferencePanes";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
LIBRARY_STYLE = Bundle;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = (
|
||||
"-bundle",
|
||||
"-twolevel_namespace",
|
||||
);
|
||||
OTHER_REZFLAGS = "";
|
||||
PRODUCT_NAME = ejabberd;
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wmost",
|
||||
"-Wno-four-char-constants",
|
||||
"-Wno-unknown-pragmas",
|
||||
);
|
||||
WRAPPER_EXTENSION = prefPane;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Deployment;
|
||||
};
|
||||
83231E650B57EBEA006434E8 /* Default */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_ENABLE_TRIGRAPHS = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = ejabberd_Prefix.pch;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = NO;
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Library/PreferencePanes";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
LIBRARY_STYLE = Bundle;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = (
|
||||
"-bundle",
|
||||
"-twolevel_namespace",
|
||||
);
|
||||
OTHER_REZFLAGS = "";
|
||||
PRODUCT_NAME = ejabberd;
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wmost",
|
||||
"-Wno-four-char-constants",
|
||||
"-Wno-unknown-pragmas",
|
||||
);
|
||||
WRAPPER_EXTENSION = prefPane;
|
||||
};
|
||||
name = Default;
|
||||
};
|
||||
83231E670B57EBEA006434E8 /* Development */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Development;
|
||||
};
|
||||
83231E680B57EBEA006434E8 /* Deployment */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Deployment;
|
||||
};
|
||||
83231E690B57EBEA006434E8 /* Default */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Default;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
83231E620B57EBEA006434E8 /* Build configuration list for PBXNativeTarget "ejabberd" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83231E630B57EBEA006434E8 /* Development */,
|
||||
83231E640B57EBEA006434E8 /* Deployment */,
|
||||
83231E650B57EBEA006434E8 /* Default */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Deployment;
|
||||
};
|
||||
83231E660B57EBEA006434E8 /* Build configuration list for PBXProject "ejabberd" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83231E670B57EBEA006434E8 /* Development */,
|
||||
83231E680B57EBEA006434E8 /* Deployment */,
|
||||
83231E690B57EBEA006434E8 /* Default */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Deployment;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 089C1669FE841209C02AAC07 /* Project object */;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ejabberdController.h
|
||||
// ejabberd preference pane
|
||||
//
|
||||
// Created on Wed Apr 19 2006.
|
||||
// Copyright (c) 2006-2009 ProcessOne.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface ejabberdController : NSObject
|
||||
{
|
||||
IBOutlet NSTextField *status;
|
||||
//IBOutlet NSImageView *image;
|
||||
IBOutlet NSImageView *imagestarted;
|
||||
IBOutlet NSImageView *imagestopped;
|
||||
IBOutlet NSProgressIndicator *actionProgress;
|
||||
IBOutlet NSButton *startStopButton;
|
||||
IBOutlet NSButton *automaticBox;
|
||||
BOOL started;
|
||||
char startscript[128];
|
||||
char stopscript[128];
|
||||
char statusscript[128];
|
||||
char waitstartedscript[128];
|
||||
char waitstoppedscript[128];
|
||||
}
|
||||
- (void)setStarted:(BOOL)flag;
|
||||
- (BOOL)isStarted;
|
||||
- (void)getRunningStatus;
|
||||
- (void)waitRunningStatus;
|
||||
- (void)updateRunningStatus;
|
||||
- (IBAction)startStopAction:(id)sender;
|
||||
- (IBAction)automaticStartAction:(id)sender;
|
||||
@end
|
|
@ -0,0 +1,125 @@
|
|||
//
|
||||
// ejabberdController.m
|
||||
// ejabberd preference pane
|
||||
//
|
||||
// Created on Wed Apr 19 2006.
|
||||
// Copyright (c) 2006-2009 ProcessOne.
|
||||
//
|
||||
|
||||
|
||||
#import "ejabberdController.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@implementation ejabberdController
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if((self = [super init])) {
|
||||
[self performSelector:@selector(getRunningStatus) withObject:self afterDelay:0.5];
|
||||
/*
|
||||
[automaticBox setState:1];
|
||||
[automaticBox setNeedsDisplay];
|
||||
NSDictionary *defaultDefaults = [[NSDictionary alloc] initWithContentsOfFile:
|
||||
[bundle pathForResource:@"ejabberdPath" ofType:@"plist"]];
|
||||
[[EjabberdPreferences preferences] registerDefaults:defaultDefaults];
|
||||
[defaultDefaults release];
|
||||
*/
|
||||
//TODO: This is very dirty code. need rewrite by MacOS/ObjC coder.
|
||||
FILE *file;
|
||||
char conf[255];
|
||||
strcpy(conf, getenv("HOME"));
|
||||
strcat(conf, "/Library/PreferencePanes/ejabberd.prefPane/Contents/Resources/config.txt");
|
||||
file = fopen(conf,"r");
|
||||
char path[255], *ptr;
|
||||
memset(path, 0, 128);
|
||||
fgets(path, 127, file);
|
||||
for(ptr=path; *ptr; ptr++)
|
||||
{
|
||||
if(*ptr==10) *ptr=0;
|
||||
if(*ptr==13) *ptr=0;
|
||||
}
|
||||
fclose(file);
|
||||
sprintf(startscript, "%s/bin/ejabberdctl start", path);
|
||||
sprintf(stopscript, "%s/bin/ejabberdctl stop", path);
|
||||
sprintf(statusscript, "%s/bin/ejabberdctl status", path);
|
||||
sprintf(waitstartedscript, "%s/bin/ejabberdctl started", path);
|
||||
sprintf(waitstoppedscript, "%s/bin/ejabberdctl stopped", path);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setStarted:(BOOL)flag
|
||||
{
|
||||
started = flag;
|
||||
}
|
||||
|
||||
- (BOOL) isStarted
|
||||
{
|
||||
return started;
|
||||
}
|
||||
|
||||
- (void) getRunningStatus
|
||||
{
|
||||
[actionProgress startAnimation:self];
|
||||
started = (system(statusscript) == 0);
|
||||
[self updateRunningStatus];
|
||||
}
|
||||
|
||||
- (void) waitRunningStatus
|
||||
{
|
||||
[actionProgress startAnimation:self];
|
||||
system(started?waitstartedscript:waitstoppedscript);
|
||||
[self getRunningStatus];
|
||||
}
|
||||
|
||||
- (void) updateRunningStatus
|
||||
{
|
||||
//[startStopButton setTitle:isStarted
|
||||
// ? NSLocalizedStringFromTableInBundle(@"Stop ejabberd",nil,bundle,@"")
|
||||
// : NSLocalizedStringFromTableInBundle(@"Start ejabberd",nil,bundle,@"")];
|
||||
if(started)
|
||||
{
|
||||
[startStopButton setTitle:@"Stop ejabberd"];
|
||||
[status setStringValue:@"ejabberd is started."];
|
||||
[imagestarted setHidden:NO];
|
||||
[imagestopped setHidden:YES];
|
||||
} else {
|
||||
[startStopButton setTitle:@"Start ejabberd"];
|
||||
[status setStringValue:@"ejabberd is stopped."];
|
||||
[imagestarted setHidden:YES];
|
||||
[imagestopped setHidden:NO];
|
||||
}
|
||||
[actionProgress stopAnimation:self];
|
||||
}
|
||||
|
||||
-(IBAction)startStopAction:(id)sender
|
||||
{
|
||||
[actionProgress startAnimation:self];
|
||||
if(started)
|
||||
{
|
||||
[status setStringValue:@"Stopping ejabberd..."];
|
||||
started = !(system(stopscript) == 0);
|
||||
} else {
|
||||
[status setStringValue:@"Starting ejabberd..."];
|
||||
started = (system(startscript) == 0);
|
||||
}
|
||||
[self performSelector:@selector(waitRunningStatus) withObject:self afterDelay:0.5];
|
||||
}
|
||||
|
||||
-(IBAction)automaticStartAction:(id)sender
|
||||
{
|
||||
//TODO: implement autostart
|
||||
if([automaticBox state])
|
||||
{
|
||||
system("mkdir -p ~/Library/LaunchDaemons");
|
||||
system("cp /Library/PreferencePanes/ejabberd.prefPane/Contents/Resources/ejabberd.plist ~/Library/LaunchDaemons");
|
||||
system("launchctl load ~/Library/LaunchDaemons/ejabberd.plist");
|
||||
} else {
|
||||
system("launchctl unload ~/Library/LaunchDaemons/ejabberd.plist");
|
||||
system("rm ~/Library/LaunchDaemons/ejabberd.plist");
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// ejabberdPref.h
|
||||
// ejabberd preference pane
|
||||
//
|
||||
// Created on Wed Apr 19 2006.
|
||||
// Copyright (c) 2006-2009 ProcessOne.
|
||||
//
|
||||
|
||||
#import <PreferencePanes/PreferencePanes.h>
|
||||
|
||||
|
||||
@interface ejabberdPref : NSPreferencePane
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void) mainViewDidLoad;
|
||||
|
||||
@end
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// ejabberdPref.m
|
||||
// ejabberd preference pane
|
||||
//
|
||||
// Created on Wed Apr 19 2006.
|
||||
// Copyright (c) 2006-2009 ProcessOne.
|
||||
//
|
||||
|
||||
#import "ejabberdPref.h"
|
||||
|
||||
|
||||
@implementation ejabberdPref
|
||||
|
||||
- (void) mainViewDidLoad
|
||||
{
|
||||
//ejabberdPref->ejabberdController
|
||||
}
|
||||
|
||||
@end
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// Prefix header for all source files of the 'ejabberd' target in the 'ejabberd' project.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildVersion</key>
|
||||
<string>54</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>ProjectName</key>
|
||||
<string>PreferencePaneTemplate</string>
|
||||
<key>SourceVersion</key>
|
||||
<string>120000</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,342 +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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General
|
||||
Public License instead of this License.
|
|
@ -1,210 +0,0 @@
|
|||
# HTTP authentication module
|
||||
|
||||
## Overview
|
||||
|
||||
The purpose of this module is to connect with an external REST API and
|
||||
delegate the authentication operations to it whenever possible. The
|
||||
component must implement the API described in one of the next sections
|
||||
for `ejabberd_auth_http` to work out of the box.
|
||||
|
||||
Thanks to ejabberd design, the user base does not have to be local and
|
||||
this approach allows you to avoid user base duplication, without
|
||||
having to grant access to your main backend database.
|
||||
|
||||
The module can be especially useful for users maintaining their own,
|
||||
central user database, which is shared with other services. It fits
|
||||
perfectly when client application uses custom authentication token and
|
||||
ejabberd has to validate it externally.
|
||||
|
||||
This module requires ejabberd 20.02 or higher.
|
||||
|
||||
## Configuration
|
||||
|
||||
### How to enable
|
||||
|
||||
The simplest way is to just replace default `auth_method` option in
|
||||
`ejabberd.yml` with `auth_method: http`.
|
||||
|
||||
However, enabling the module **is not enough!** Please follow
|
||||
instructions below.
|
||||
|
||||
### Configuration options
|
||||
|
||||
`ejabberd_auth_http` requires some parameters to function
|
||||
properly. The following options can be set in `auth_opts` in
|
||||
`ejabberd.yml`:
|
||||
|
||||
* `host` (mandatory, `string`) - consists of protocol, hostname (or IP) and port (optional). Examples:
|
||||
* `host: "http://localhost:12000"`
|
||||
* `host: "https://10.20.30.40"`
|
||||
* `connection_pool_size` (optional, `integer`, default: 10) - the
|
||||
number of connections open to auth service
|
||||
* `connection_opts` (optional, default: `[]`) - extra options for
|
||||
hackers: http://erlang.org/doc/man/gen_tcp.html#type-connect_option
|
||||
|
||||
* `basic_auth` (optional, default: `""`) - HTTP Basic Authentication
|
||||
in format `"username:password"`; auth service doesn't have to
|
||||
require authentication for HTTP auth to work
|
||||
|
||||
* `path_prefix` (optional, default: `"/"`) - a path prefix to be
|
||||
inserted between `host` and method name; must be terminated with `/`
|
||||
|
||||
Example configuration:
|
||||
```
|
||||
auth_method: http
|
||||
auth_opts:
|
||||
host: "http://localhost:12000"
|
||||
connection_pool_size: 10
|
||||
connection_opts: []
|
||||
basic_auth: ""
|
||||
path_prefix: "/"
|
||||
```
|
||||
|
||||
## SCRAM support
|
||||
|
||||
`ejabberd_auth_http` can use the SCRAM method. When SCRAM is enabled,
|
||||
the passwords sent to the auth service are serialised and the same
|
||||
serialised format is expected when fetching a password from the
|
||||
component.
|
||||
|
||||
It is transparent when ejabberd is responsible for all DB operations
|
||||
such as password setting, account creation etc.
|
||||
|
||||
The service CAN perform the (de)serialisation of SCRAM-encoded
|
||||
passwords, using a format that will be described in near future in
|
||||
separate document.
|
||||
|
||||
## Authentication service API
|
||||
|
||||
All GET requests include the following URL-encoded query string:
|
||||
`?user=<username>&server=<domain>&pass=<password>`.
|
||||
|
||||
All POST requests have the following URL-encoded string in the request
|
||||
body: `user=<username>&server=<domain>&pass=<password>`.
|
||||
|
||||
If certain method does not need a password, the value of `pass` is
|
||||
**undefined**, so it shouldn't be used.
|
||||
|
||||
For the best integration, the return code range should not exceed the
|
||||
list below:
|
||||
|
||||
* 500 - internal server error
|
||||
* 409 - conflict
|
||||
* 404 - not found
|
||||
* 403 - not allowed
|
||||
* 401 - not authorised
|
||||
* 400 - other error, should be sent in response body
|
||||
* 204 - success, no return data
|
||||
* 201 - created
|
||||
* 200 - success, return value in response body
|
||||
|
||||
Whenever the specification says "anything else", service should use
|
||||
one of the codes from the list above.
|
||||
|
||||
Some requests consider multiple return codes a "success". It is up to
|
||||
the server-side developer to pick one of the codes.
|
||||
|
||||
### `check_password`
|
||||
|
||||
* **Method:** GET
|
||||
* **Type:** mandatory when SCRAM is not used
|
||||
* **Return values:**
|
||||
* 200, `true` or `false` in body
|
||||
* anything else - will be treated as `false`
|
||||
* **Description:** Must respond if the password is valid for user.
|
||||
|
||||
### `get_password`
|
||||
|
||||
* **Method:** GET
|
||||
* **Type:** mandatory when SCRAM or DIGEST SASL mechanism is used
|
||||
* **Return values:**
|
||||
* 200, password in the body
|
||||
* anything else - `get_password` will fail
|
||||
* **Description:** Must return the user's password in plaintext or in the SCRAM serialised form.
|
||||
|
||||
### `user_exists`
|
||||
|
||||
* **Method:** GET
|
||||
* **Type:** mandatory
|
||||
* **Return values:**
|
||||
* 200, `true` or `false` in body
|
||||
* anything else - will be treated as `false`
|
||||
* **Description:** Must return the information whether the user exists in DB.
|
||||
|
||||
### `set_password`
|
||||
|
||||
* **Method:** POST
|
||||
* **Type:** mandatory when `mod_register` is enabled
|
||||
* **Return values:**
|
||||
* 200 or 201 or 204 - success
|
||||
* anything else - will be treated as `false`
|
||||
* **Description:** Must set user's password in the internal database
|
||||
to a provided value. The value should not be transformed (except for
|
||||
URL-decoding) before writing into DB.
|
||||
|
||||
### `remove_user`
|
||||
|
||||
* **Method:** POST
|
||||
* **Type:** mandatory when `mod_register` is enabled
|
||||
* **Return values:**
|
||||
* 200 or 201 or 204 - success
|
||||
* 404 - user does not exist
|
||||
* 403 - not allowed for some reason
|
||||
* 40X - will be treated as `bad request`
|
||||
* **Description:** Removes a user account.
|
||||
|
||||
### `remove_user_validate`
|
||||
|
||||
* **Method:** POST
|
||||
* **Type:** mandatory when `mod_register` is enabled
|
||||
* **Return values:**
|
||||
* 200 or 201 or 204 - success
|
||||
* 404 - user does not exist
|
||||
* 403 - invalid user password or not allowed for other reason
|
||||
* 40X - will be treated as `bad request`
|
||||
* **Description:** Removes a user account only if provided password is valid.
|
||||
|
||||
### `register`
|
||||
|
||||
* **Method:** POST
|
||||
* **Type:** mandatory when `mod_register` is enabled
|
||||
* **Return values:**
|
||||
* 201 - success
|
||||
* 409 - user already exists
|
||||
* anything else - will be treated as failure
|
||||
* **Description:** Creates a user account.
|
||||
|
||||
## Authentication service API recipes
|
||||
|
||||
Below are some examples of the auth service APIs and ejabberd-side
|
||||
configuration along with use cases.
|
||||
|
||||
### System using common, custom auth token
|
||||
|
||||
An Auth token is provided as a password.
|
||||
|
||||
* **Service implements:** `check_password`, `user_exists`
|
||||
* **ejabberd config:** `auth_password format`: `plain`, `mod_register` disabled
|
||||
* **Client side:** MUST NOT use `DIGEST-MD5` mechanism; use `PLAIN`
|
||||
|
||||
### Central database of plaintext passwords
|
||||
|
||||
* **Service implements:** `check_password`, `get_password`, `user_exists`
|
||||
* **ejabberd config:** `auth_password_format`: `plain`, `mod_register` disabled
|
||||
* **Client side:** May use any available auth method
|
||||
|
||||
### Central database able to process SCRAM
|
||||
|
||||
* **Service implements:** `get_password`, `user_exists`
|
||||
* **ejabberd config:** `auth_password_format`: `scram`, `mod_register` disabled
|
||||
* **Client side:** May use any available auth method
|
||||
|
||||
### All-included
|
||||
|
||||
* **Service implements:** all methods
|
||||
* **ejabberd config:** `auth_password_format`: `scram` (recommended) or `plain`, `mod_register` enabled
|
||||
* **Client side:** May use any available auth method
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the service supports multiple passwords per user you need to disable authentication caching with `auth_use_cache: false`. See [https://github.com/processone/ejabberd-contrib/issues/288](https://github.com/processone/ejabberd-contrib/issues/288).
|
|
@ -1,5 +0,0 @@
|
|||
author: "Piotr Nosek <piotr.nosek at erlang-solutions.com>"
|
||||
category: "auth"
|
||||
summary: "Authentication via HTTP request"
|
||||
home: "https://github.com/processone/ejabberd-contrib/tree/master/"
|
||||
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -1,4 +0,0 @@
|
|||
{deps, [
|
||||
{cuesport, ".*", {git, "https://github.com/goj/cuesport"}},
|
||||
{fusco, ".*", {git, "https://github.com/esl/fusco"}}
|
||||
]}.
|
|
@ -1,290 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_http.erl
|
||||
%%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
|
||||
%%% Purpose : Authentication via HTTP request
|
||||
%%% Created : 23 Sep 2013 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_http).
|
||||
-author('piotr.nosek@erlang-solutions.com').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
set_password/3,
|
||||
check_password/4,
|
||||
check_password/6,
|
||||
try_register/3,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/1,
|
||||
store_type/1,
|
||||
login/2,
|
||||
get_password/3,
|
||||
stop/1]).
|
||||
|
||||
-include_lib("xmpp/include/scram.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-spec start(binary()) -> ok.
|
||||
start(Host) ->
|
||||
AuthOpts = ejabberd_config:get_option({auth_opts, Host}),
|
||||
{_, AuthHost} = lists:keyfind(host, 1, AuthOpts),
|
||||
PoolSize = proplists:get_value(connection_pool_size, AuthOpts, 10),
|
||||
Opts = proplists:get_value(connection_opts, AuthOpts, []),
|
||||
ChildMods = [fusco],
|
||||
ChildMFA = {fusco, start_link, [binary_to_list(AuthHost), Opts]},
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
ChildSpec = {Proc, {cuesport, start_link,
|
||||
[pool_name(Host), PoolSize, ChildMods, ChildMFA]},
|
||||
transient, 2000, supervisor, [cuesport | ChildMods]},
|
||||
supervisor:start_child(ejabberd_backend_sup, ChildSpec),
|
||||
ok.
|
||||
|
||||
-spec plain_password_required(binary()) -> false.
|
||||
plain_password_required(_Server) ->
|
||||
false.
|
||||
|
||||
-spec store_type(binary()) -> external.
|
||||
store_type(_) ->
|
||||
external.
|
||||
|
||||
-spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean()}.
|
||||
check_password(LUser, _AuthzId, LServer, Password) ->
|
||||
case scram2:enabled(LServer) of
|
||||
false ->
|
||||
case make_req(get, <<"check_password">>, LUser, LServer, Password) of
|
||||
{ok, <<"true">>} -> {cache, true};
|
||||
_ -> {nocache, false}
|
||||
end;
|
||||
true ->
|
||||
case verify_scram_password(LUser, LServer, Password) of
|
||||
{ok, true} -> {cache, true};
|
||||
_ -> {nocache, false}
|
||||
end
|
||||
end.
|
||||
|
||||
-spec check_password(binary(), binary(), binary(), binary(), binary(), fun()) -> boolean().
|
||||
check_password(LUser, _AuthzId, LServer, Password, Digest, DigestGen) ->
|
||||
case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
|
||||
{error, _} ->
|
||||
false;
|
||||
{ok, GotPasswd} ->
|
||||
case scram2:enabled(LServer) of
|
||||
true ->
|
||||
case scram2:deserialize(GotPasswd) of
|
||||
{ok, #scram{} = Scram} ->
|
||||
scram2:check_digest(Scram, Digest, DigestGen, Password);
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
false ->
|
||||
check_digest(Digest, DigestGen, Password, GotPasswd)
|
||||
end
|
||||
end.
|
||||
|
||||
-spec check_digest(binary(), fun(), binary(), binary()) -> boolean().
|
||||
check_digest(Digest, DigestGen, Password, Passwd) ->
|
||||
DigRes = if
|
||||
Digest /= <<>> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= <<>>)
|
||||
end.
|
||||
|
||||
|
||||
-spec set_password(binary(), binary(), binary()) -> {ets_cache:tag(), {ok, binary()} | {error, not_allowed}}.
|
||||
set_password(LUser, LServer, Password) ->
|
||||
PasswordFinal = case scram2:enabled(LServer) of
|
||||
true -> scram2:serialize(scram2:password_to_scram(
|
||||
Password, scram2:iterations(LServer)));
|
||||
false -> Password
|
||||
end,
|
||||
case make_req(post, <<"set_password">>, LUser, LServer, PasswordFinal) of
|
||||
{error, _Error} -> {nocache, {error, not_allowed}};
|
||||
{ok, _} -> {cache, {ok, Password}}
|
||||
end.
|
||||
|
||||
-spec try_register(binary(), binary(), binary()) -> {ets_cache:tag(), {ok, binary()} | {error, exists | not_allowed}}.
|
||||
try_register(LUser, LServer, Password) ->
|
||||
PasswordFinal = case scram2:enabled(LServer) of
|
||||
true -> scram2:serialize(scram2:password_to_scram(
|
||||
Password, scram2:iterations(LServer)));
|
||||
false -> Password
|
||||
end,
|
||||
case make_req(post, <<"register">>, LUser, LServer, PasswordFinal) of
|
||||
{ok, <<"created">>} -> {cache, {ok, Password}};
|
||||
{error, conflict} -> {nocache, {error, exists}};
|
||||
_Error -> {nocache, {error, not_allowed}}
|
||||
end.
|
||||
|
||||
-spec get_password(binary(), binary()) -> {cache, error}.
|
||||
get_password(_, _) ->
|
||||
{cache, error}.
|
||||
|
||||
-spec get_password_s(binary(), binary()) -> {cache, error}.
|
||||
get_password_s(_User, _Server) ->
|
||||
{cache, error}.
|
||||
|
||||
-spec user_exists(binary(), binary()) -> {ets_cache:tag(), boolean()}.
|
||||
user_exists(LUser, LServer) ->
|
||||
case make_req(get, <<"user_exists">>, LUser, LServer, <<"">>) of
|
||||
{ok, <<"true">>} -> {cache, true};
|
||||
_ -> {nocache, false}
|
||||
end.
|
||||
|
||||
-spec remove_user(binary(), binary()) -> ok | {error, db_failure | not_allowed}.
|
||||
remove_user(LUser, LServer) ->
|
||||
remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>).
|
||||
|
||||
-spec remove_user(binary(), binary(), binary()) -> ok | {error, db_failure | not_allowed}.
|
||||
remove_user(LUser, LServer, Password) ->
|
||||
case scram2:enabled(LServer) of
|
||||
false ->
|
||||
remove_user_req(LUser, LServer, Password, <<"remove_user_validate">>);
|
||||
true ->
|
||||
case verify_scram_password(LUser, LServer, Password) of
|
||||
{ok, false} ->
|
||||
{error, not_allowed};
|
||||
{ok, true} ->
|
||||
remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>);
|
||||
{error, _Error} ->
|
||||
{error, db_failure}
|
||||
end
|
||||
end.
|
||||
|
||||
-spec remove_user_req(binary(), binary(), binary(), binary()) ->
|
||||
ok | {error, not_allowed | db_failure}.
|
||||
remove_user_req(LUser, LServer, Password, Method) ->
|
||||
case make_req(post, Method, LUser, LServer, Password) of
|
||||
{error, not_allowed} -> {error, not_allowed};
|
||||
{error, not_found} -> {error, db_failure};
|
||||
{error, _} -> {error, db_failure};
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Request maker
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-ifdef(OTP_BELOW_25).
|
||||
-dialyzer({no_missing_calls, [uri_quote/1]}).
|
||||
uri_quote(URL) ->
|
||||
http_uri:encode(URL).
|
||||
-else.
|
||||
uri_quote(URL) ->
|
||||
uri_string:quote(URL). % Available since OTP 25.0
|
||||
-endif.
|
||||
|
||||
-spec make_req(post | get, binary(), binary(), binary(), binary()) ->
|
||||
{ok, Body :: binary()} | {error, term()}.
|
||||
make_req(_, _, LUser, LServer, _) when LUser == error orelse LServer == error ->
|
||||
{error, {prep_failed, LUser, LServer}};
|
||||
make_req(Method, Path, LUser, LServer, Password) ->
|
||||
AuthOpts = ejabberd_config:get_option({auth_opts, LServer}),
|
||||
BasicAuth = case lists:keyfind(basic_auth, 1, AuthOpts) of
|
||||
{_, BasicAuth0} -> BasicAuth0;
|
||||
_ -> ""
|
||||
end,
|
||||
PathPrefix = case lists:keyfind(path_prefix, 1, AuthOpts) of
|
||||
{_, Prefix} -> Prefix;
|
||||
false -> <<"/">>
|
||||
end,
|
||||
BasicAuth64 = base64:encode(BasicAuth),
|
||||
LUserE = list_to_binary(uri_quote(binary_to_list(LUser))),
|
||||
LServerE = list_to_binary(uri_quote(binary_to_list(LServer))),
|
||||
PasswordE = list_to_binary(uri_quote(binary_to_list(Password))),
|
||||
Query = <<"user=", LUserE/binary, "&server=", LServerE/binary, "&pass=", PasswordE/binary>>,
|
||||
Header = [{<<"Authorization">>, <<"Basic ", BasicAuth64/binary>>}],
|
||||
ContentType = {<<"Content-Type">>, <<"application/x-www-form-urlencoded">>},
|
||||
Connection = cuesport:get_worker(existing_pool_name(LServer)),
|
||||
|
||||
?DEBUG("Making request '~s' for user ~s@~s...", [Path, LUser, LServer]),
|
||||
{Url, MethodStr, Headers, Query2} =
|
||||
case Method of
|
||||
get -> {<<PathPrefix/binary, Path/binary, "?", Query/binary>>,
|
||||
"GET",
|
||||
Header,
|
||||
""};
|
||||
post -> {<<PathPrefix/binary, Path/binary>>,
|
||||
"POST",
|
||||
[ContentType|Header],
|
||||
Query}
|
||||
end,
|
||||
http_request(Connection, Url, MethodStr, Headers, Query2, 0).
|
||||
|
||||
http_request(Connection, Url, MethodStr, Headers, Query, RedirectCounter) ->
|
||||
{ok, {{Code, _Reason}, RespHeaders, RespBody, _, _}} =
|
||||
fusco:request(Connection, Url, MethodStr, Headers, Query, 2, 5000),
|
||||
?DEBUG("Request result: ~s: ~p", [Code, RespBody]),
|
||||
case Code of
|
||||
<<"409">> -> {error, conflict};
|
||||
<<"404">> -> {error, not_found};
|
||||
<<"401">> -> {error, not_authorized};
|
||||
<<"403">> -> {error, not_allowed};
|
||||
<<"400">> -> {error, RespBody};
|
||||
<<"503">> -> {error, RespBody};
|
||||
<<"204">> -> {ok, <<"">>};
|
||||
<<"201">> -> {ok, <<"created">>};
|
||||
<<"200">> -> {ok, RespBody};
|
||||
R when (R==<<"301">>) or (R==<<"307">>) or (R==<<"308">>) ->
|
||||
handle_redirect(RespHeaders, Connection, MethodStr, Headers, Query, RedirectCounter+1);
|
||||
_ -> {error, RespBody}
|
||||
end.
|
||||
|
||||
handle_redirect(RespHeaders, Connection, MethodStr, Headers, Query, RedirectCounter)
|
||||
when RedirectCounter < 5 ->
|
||||
{_, Location} = lists:keyfind(<<"location">>, 1, RespHeaders),
|
||||
http_request(Connection, Location, MethodStr, Headers, Query, RedirectCounter);
|
||||
handle_redirect(_, _, _, _, _, _) ->
|
||||
{error, redirect_loop}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Other internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
-spec pool_name(binary()) -> atom().
|
||||
pool_name(Host) ->
|
||||
list_to_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
|
||||
|
||||
-spec existing_pool_name(binary()) -> atom().
|
||||
existing_pool_name(Host) ->
|
||||
list_to_existing_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
|
||||
|
||||
-spec verify_scram_password(binary(), binary(), binary()) ->
|
||||
{ok, boolean()} | {error, bad_request | not_exists}.
|
||||
verify_scram_password(LUser, LServer, Password) ->
|
||||
case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
|
||||
{ok, RawPassword} ->
|
||||
case scram2:deserialize(RawPassword) of
|
||||
{ok, #scram{} = ScramRecord} ->
|
||||
{ok, scram2:check_password(Password, ScramRecord)};
|
||||
_ ->
|
||||
{error, bad_request}
|
||||
end;
|
||||
_ ->
|
||||
{error, not_exists}
|
||||
end.
|
||||
|
||||
login(_User, _Server) ->
|
||||
erlang:error(not_implemented).
|
||||
|
||||
get_password(_User, _Server, _DefaultValue) ->
|
||||
erlang:error(not_implemented).
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
supervisor:terminate_child(ejabberd_backend_sup, Proc),
|
||||
supervisor:delete_child(ejabberd_backend_sup, Proc).
|
|
@ -1,192 +0,0 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : scram.erl
|
||||
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%% Purpose : SCRAM (RFC 5802)
|
||||
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
|
||||
%%%
|
||||
%%% 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
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(scram2).
|
||||
|
||||
-author('stephen.roettger@googlemail.com').
|
||||
|
||||
-include_lib("xmpp/include/scram.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
%% External exports
|
||||
%% ejabberd doesn't implement SASLPREP, so we use the similar RESOURCEPREP instead
|
||||
-export([ % Core SCRAM functions
|
||||
salted_password/3,
|
||||
stored_key/1,
|
||||
server_key/1,
|
||||
server_signature/2,
|
||||
client_signature/2,
|
||||
client_key/1,
|
||||
client_key/2]).
|
||||
|
||||
-export([
|
||||
enabled/1,
|
||||
iterations/0,
|
||||
iterations/1,
|
||||
password_to_scram/1,
|
||||
password_to_scram/2,
|
||||
check_password/2,
|
||||
check_digest/4
|
||||
]).
|
||||
|
||||
-export([serialize/1, deserialize/1]).
|
||||
|
||||
-export([scram_to_tuple/1]).
|
||||
|
||||
-define(SALT_LENGTH, 16).
|
||||
-define(SCRAM_SERIAL_PREFIX, "==SCRAM==,").
|
||||
|
||||
-spec salted_password(binary(), binary(), non_neg_integer()) -> binary().
|
||||
salted_password(Password, Salt, IterationCount) ->
|
||||
hi(jid:resourceprep(Password), Salt, IterationCount).
|
||||
|
||||
-spec client_key(binary()) -> binary().
|
||||
client_key(SaltedPassword) ->
|
||||
crypto_hmac(sha, SaltedPassword, <<"Client Key">>).
|
||||
|
||||
-spec stored_key(binary()) -> binary().
|
||||
stored_key(ClientKey) -> crypto:hash(sha, ClientKey).
|
||||
|
||||
-spec server_key(binary()) -> binary().
|
||||
server_key(SaltedPassword) ->
|
||||
crypto_hmac(sha, SaltedPassword, <<"Server Key">>).
|
||||
|
||||
-spec client_signature(binary(), binary()) -> binary().
|
||||
client_signature(StoredKey, AuthMessage) ->
|
||||
crypto_hmac(sha, StoredKey, AuthMessage).
|
||||
|
||||
-spec client_key(binary(), binary()) -> binary().
|
||||
client_key(ClientProof, ClientSignature) ->
|
||||
list_to_binary(lists:zipwith(fun (X, Y) -> X bxor Y end,
|
||||
binary_to_list(ClientProof),
|
||||
binary_to_list(ClientSignature))).
|
||||
|
||||
-spec server_signature(binary(), binary()) -> binary().
|
||||
server_signature(ServerKey, AuthMessage) ->
|
||||
crypto_hmac(sha, ServerKey, AuthMessage).
|
||||
|
||||
hi(Password, Salt, IterationCount) ->
|
||||
U1 = crypto_hmac(sha, Password, <<Salt/binary, 0, 0, 0, 1>>),
|
||||
list_to_binary(lists:zipwith(fun (X, Y) -> X bxor Y end,
|
||||
binary_to_list(U1),
|
||||
binary_to_list(hi_round(Password, U1,
|
||||
IterationCount - 1)))).
|
||||
|
||||
hi_round(Password, UPrev, 1) ->
|
||||
crypto_hmac(sha, Password, UPrev);
|
||||
hi_round(Password, UPrev, IterationCount) ->
|
||||
U = crypto_hmac(sha, Password, UPrev),
|
||||
list_to_binary(lists:zipwith(fun (X, Y) -> X bxor Y end,
|
||||
binary_to_list(U),
|
||||
binary_to_list(hi_round(Password, U,
|
||||
IterationCount - 1)))).
|
||||
|
||||
|
||||
enabled(Host) ->
|
||||
case ejabberd_config:get_option({auth_opts, Host}) of
|
||||
undefined ->
|
||||
false;
|
||||
AuthOpts ->
|
||||
{password_format, scram} == lists:keyfind(password_format, 1, AuthOpts)
|
||||
end.
|
||||
|
||||
iterations() -> ?SCRAM_DEFAULT_ITERATION_COUNT.
|
||||
|
||||
iterations(Host) ->
|
||||
case ejabberd_config:get_option({auth_opts, Host}) of
|
||||
undefined ->
|
||||
iterations();
|
||||
AuthOpts ->
|
||||
case lists:keyfind(scram_iterations, 1, AuthOpts) of
|
||||
false -> iterations();
|
||||
{_, Iterations} -> Iterations
|
||||
end
|
||||
end.
|
||||
|
||||
password_to_scram(Password) ->
|
||||
password_to_scram(Password, ?SCRAM_DEFAULT_ITERATION_COUNT).
|
||||
|
||||
password_to_scram(#scram{} = Password, _) ->
|
||||
Password;
|
||||
password_to_scram(Password, IterationCount) ->
|
||||
Salt = p1_rand:bytes(?SALT_LENGTH),
|
||||
SaltedPassword = salted_password(Password, Salt, IterationCount),
|
||||
StoredKey = stored_key(scram2:client_key(SaltedPassword)),
|
||||
ServerKey = server_key(SaltedPassword),
|
||||
#scram{storedkey = base64:encode(StoredKey),
|
||||
serverkey = base64:encode(ServerKey),
|
||||
salt = base64:encode(Salt),
|
||||
iterationcount = IterationCount}.
|
||||
|
||||
check_password(Password, Scram) ->
|
||||
IterationCount = Scram#scram.iterationcount,
|
||||
Salt = base64:decode(Scram#scram.salt),
|
||||
SaltedPassword = salted_password(Password, Salt, IterationCount),
|
||||
StoredKey = stored_key(client_key(SaltedPassword)),
|
||||
(base64:decode(Scram#scram.storedkey) == StoredKey).
|
||||
|
||||
serialize(#scram{storedkey = StoredKey, serverkey = ServerKey,
|
||||
salt = Salt, iterationcount = IterationCount})->
|
||||
IterationCountBin = integer_to_binary(IterationCount),
|
||||
<< <<?SCRAM_SERIAL_PREFIX>>/binary,
|
||||
StoredKey/binary,$,,ServerKey/binary,
|
||||
$,,Salt/binary,$,,IterationCountBin/binary>>.
|
||||
|
||||
deserialize(<<?SCRAM_SERIAL_PREFIX, Serialized/binary>>) ->
|
||||
case catch binary:split(Serialized, <<",">>, [global]) of
|
||||
[StoredKey, ServerKey,Salt,IterationCount] ->
|
||||
{ok, #scram{storedkey = StoredKey,
|
||||
serverkey = ServerKey,
|
||||
salt = Salt,
|
||||
iterationcount = binary_to_integer(IterationCount)}};
|
||||
_ ->
|
||||
?WARNING_MSG("Incorrect serialized SCRAM: ~p, ~p", [Serialized]),
|
||||
{error, incorrect_scram}
|
||||
end;
|
||||
deserialize(Bin) ->
|
||||
?WARNING_MSG("Corrupted serialized SCRAM: ~p, ~p", [Bin]),
|
||||
{error, corrupted_scram}.
|
||||
|
||||
-spec scram_to_tuple(scram()) -> {binary(), binary(), binary(), non_neg_integer()}.
|
||||
scram_to_tuple(Scram) ->
|
||||
{base64:decode(Scram#scram.storedkey),
|
||||
base64:decode(Scram#scram.serverkey),
|
||||
base64:decode(Scram#scram.salt),
|
||||
Scram#scram.iterationcount}.
|
||||
|
||||
-spec check_digest(scram(), binary(), fun(), binary()) -> boolean().
|
||||
check_digest(#scram{storedkey = StoredKey}, Digest, DigestGen, Password) ->
|
||||
Passwd = base64:decode(StoredKey),
|
||||
DigRes = if Digest /= <<"">> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true -> false
|
||||
end,
|
||||
if DigRes -> true;
|
||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||
end.
|
||||
|
||||
crypto_hmac(sha, Key, Data) ->
|
||||
misc:crypto_hmac(sha, Key, Data).
|
|
@ -1,41 +0,0 @@
|
|||
ejabberd observer_cli plugins
|
||||
=============================
|
||||
|
||||
[observer_cli][oc] is an erlang tool to
|
||||
visualize Erlang node statistics on the command line.
|
||||
|
||||
This directory contains several plugins
|
||||
specifically written to view statistics of [ejabberd][ej].
|
||||
|
||||
To install this, just run:
|
||||
```bash
|
||||
ejabberdctl module_install ejabberd_observer_cli
|
||||
```
|
||||
It will automatically download, compile and install the required dependencies:
|
||||
[observer_cli][oc], [recon][recon],
|
||||
and the additional plugin [os_stats][os].
|
||||
|
||||
Then, start an erlang shell attached to your ejabberd node, for example:
|
||||
```bash
|
||||
ejabberdctl debug
|
||||
```
|
||||
in that erlang shell execute:
|
||||
```erlang
|
||||
ejabberd_observer_cli:start().
|
||||
```
|
||||
|
||||
If using Elixir (for example when started with `ejabberdctl iexdebug`, run:
|
||||
```elixir
|
||||
:ejabberd_observer_cli.start()
|
||||
```
|
||||
|
||||
To sort columns or change between the different pages,
|
||||
type the corresponding letter and hit Enter.
|
||||
For example, to view MUC statistics, type: `M and then Enter`.
|
||||
|
||||
There is no configuration required.
|
||||
|
||||
[oc]: https://github.com/zhongwencool/observer_cli
|
||||
[os]: https://github.com/zhongwencool/os_stats
|
||||
[recon]: https://github.com/ferd/recon
|
||||
[ej]: https://www.ejabberd.im/
|
|
@ -1,5 +0,0 @@
|
|||
author: "Badlop <badlop at process-one.net>"
|
||||
category: "stats"
|
||||
summary: "Observer CLI plugins for ejabberd"
|
||||
home: "https://github.com/processone/ejabberd-contrib/tree/master/"
|
||||
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -1,5 +0,0 @@
|
|||
{deps, [
|
||||
{observer_cli, ".*", {git, "https://github.com/zhongwencool/observer_cli"}},
|
||||
{os_stats, ".*", {git, "https://github.com/zhongwencool/os_stats"}},
|
||||
{recon, ".*", {git, "https://github.com/ferd/recon"}}
|
||||
]}.
|
|
@ -1,44 +0,0 @@
|
|||
-module(ejabberd_observer_cli).
|
||||
|
||||
-export([start/0, mod_status/0]).
|
||||
|
||||
start() ->
|
||||
application:set_env(observer_cli, plugins, plugins(), [{persistent, true}]),
|
||||
observer_cli:start_plugin().
|
||||
|
||||
mod_status() ->
|
||||
"In an erlang shell run: ejabberd_observer_cli:start().".
|
||||
|
||||
plugins() ->
|
||||
[
|
||||
#{
|
||||
module => ejabberd_observer_cli_vhosts,
|
||||
title => "VHosts",
|
||||
interval => 1600,
|
||||
shortcut => "V",
|
||||
sort_column => 2
|
||||
},
|
||||
#{
|
||||
module => ejabberd_observer_cli_users,
|
||||
title => "Users",
|
||||
interval => 1600,
|
||||
shortcut => "U",
|
||||
sort_column => 2
|
||||
},
|
||||
%% #{module => ejabberd_observer_cli_userstophost, title => "Users Top Vhost",
|
||||
%% interval => 1600, shortcut => "T", sort_column => 2},
|
||||
#{
|
||||
module => ejabberd_observer_cli_muc,
|
||||
title => "MUC",
|
||||
interval => 1600,
|
||||
shortcut => "M",
|
||||
sort_column => 2
|
||||
},
|
||||
#{
|
||||
module => os_stats_plug,
|
||||
title => "OS",
|
||||
interval => 2000,
|
||||
shortcut => "O",
|
||||
sort_column => 2
|
||||
}
|
||||
].
|
|
@ -1,48 +0,0 @@
|
|||
-module(ejabberd_observer_cli_muc).
|
||||
|
||||
%% observer_cli_plugin Callback API
|
||||
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||
|
||||
attributes(PrevState) ->
|
||||
OnlineRoomsNumber = lists:foldl(
|
||||
fun(Host, Acc) ->
|
||||
Acc + mod_muc:count_online_rooms(Host)
|
||||
end,
|
||||
0,
|
||||
mod_muc_admin:find_hosts(global)
|
||||
),
|
||||
|
||||
Attrs = [
|
||||
[
|
||||
#{content => "MUC Rooms", width => 25},
|
||||
#{content => OnlineRoomsNumber, width => 8}
|
||||
]
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Attrs, NewState}.
|
||||
|
||||
sheet_header() ->
|
||||
[
|
||||
#{title => "Room Name", width => 20, shortcut => "n"},
|
||||
#{title => "MUC Service", width => 30, shortcut => "s"},
|
||||
#{title => "Occupants", width => 12, shortcut => "o"},
|
||||
#{title => "Subscribers", width => 13, shortcut => "r"}
|
||||
].
|
||||
|
||||
sheet_body(PrevState) ->
|
||||
Body = [
|
||||
begin
|
||||
{Name, Service, _} = jid:split(jid:decode(RoomStr)),
|
||||
OccupantsNumber = mod_muc_admin:get_room_occupants_number(Name, Service),
|
||||
SubsNumber = length(mod_muc_admin:get_subscribers(Name, Service)),
|
||||
[
|
||||
Name,
|
||||
Service,
|
||||
OccupantsNumber,
|
||||
SubsNumber
|
||||
]
|
||||
end
|
||||
|| RoomStr <- mod_muc_admin:muc_online_rooms(global)
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Body, NewState}.
|
|
@ -1,95 +0,0 @@
|
|||
-module(ejabberd_observer_cli_users).
|
||||
|
||||
%% observer_cli_plugin Callback API
|
||||
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||
|
||||
attributes(PrevState) ->
|
||||
Hosts = length(ejabberd_option:hosts()),
|
||||
RegisteredUsers =
|
||||
lists:foldl(
|
||||
fun(Host, Sum) ->
|
||||
ejabberd_auth:count_users(Host) + Sum
|
||||
end,
|
||||
0,
|
||||
ejabberd_option:hosts()
|
||||
),
|
||||
Sessions = length(ejabberd_sm:dirty_get_sessions_list()),
|
||||
SessionsThisNode = length(ejabberd_sm:dirty_get_my_sessions_list()),
|
||||
|
||||
Attrs = [
|
||||
[
|
||||
#{content => "Virtual Hosts", width => 18},
|
||||
#{content => Hosts, width => 8},
|
||||
#{content => "Sessions Total", width => 18},
|
||||
#{content => Sessions, width => 8}
|
||||
],
|
||||
[
|
||||
#{content => "Accounts Total", width => 18},
|
||||
#{content => RegisteredUsers, width => 8},
|
||||
#{content => "Sessions This Node", width => 18},
|
||||
#{content => SessionsThisNode, width => 8}
|
||||
]
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Attrs, NewState}.
|
||||
|
||||
sheet_header() ->
|
||||
[
|
||||
#{title => "Username", width => 25, shortcut => "u"},
|
||||
#{title => "Host", width => 25, shortcut => "h"},
|
||||
#{title => "Sessions", width => 11, shortcut => "s"},
|
||||
#{title => "Roster", width => 9, shortcut => "r"},
|
||||
#{title => "Offline", width => 10, shortcut => "o"},
|
||||
#{title => "Last Activity", width => 20, shortcut => "l"}
|
||||
].
|
||||
|
||||
sheet_body(PrevState) ->
|
||||
Body = [
|
||||
begin
|
||||
[
|
||||
Username,
|
||||
Host,
|
||||
length(ejabberd_sm:get_user_resources(Username, Host)),
|
||||
length(mod_roster:get_roster(Username, Host)),
|
||||
mod_offline:count_offline_messages(Username, Host),
|
||||
get_last_activity(Username, Host)
|
||||
]
|
||||
end
|
||||
|| {Username, Host} <- lists:sort(ejabberd_auth:get_users())
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Body, NewState}.
|
||||
|
||||
%% Code copied from ejabberd_web_admin.erl
|
||||
get_last_activity(User, Server) ->
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
case get_last_info(User, Server) of
|
||||
not_found ->
|
||||
"Never";
|
||||
{ok, Shift, _Status} ->
|
||||
TimeStamp = {Shift div 1000000, Shift rem 1000000, 0},
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||
calendar:now_to_local_time(TimeStamp),
|
||||
(io_lib:format(
|
||||
"~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
|
||||
[
|
||||
Year,
|
||||
Month,
|
||||
Day,
|
||||
Hour,
|
||||
Minute,
|
||||
Second
|
||||
]
|
||||
))
|
||||
end;
|
||||
_ ->
|
||||
"Online"
|
||||
end.
|
||||
get_last_info(User, Server) ->
|
||||
case gen_mod:is_loaded(Server, mod_last) of
|
||||
true ->
|
||||
mod_last:get_last_info(User, Server);
|
||||
false ->
|
||||
not_found
|
||||
end.
|
|
@ -1,61 +0,0 @@
|
|||
-module(ejabberd_observer_cli_userstophost).
|
||||
|
||||
%% observer_cli_plugin Callback API
|
||||
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||
|
||||
get_top_host() ->
|
||||
lists:foldl(
|
||||
fun(Host, {HostRelativeMax, CountRelativeMax}) ->
|
||||
case ejabberd_auth:count_users(Host) of
|
||||
Count when Count > CountRelativeMax ->
|
||||
{Host, Count};
|
||||
_ ->
|
||||
{HostRelativeMax, CountRelativeMax}
|
||||
end
|
||||
end,
|
||||
{unknown, -1},
|
||||
ejabberd_option:hosts()
|
||||
).
|
||||
|
||||
attributes(PrevState) ->
|
||||
{Host, _} = get_top_host(),
|
||||
RegisteredUsers = ejabberd_auth:count_users(Host),
|
||||
Sessions = length(ejabberd_sm:dirty_get_sessions_list()),
|
||||
SessionsThisNode = length(ejabberd_sm:dirty_get_my_sessions_list()),
|
||||
|
||||
Attrs = [
|
||||
[
|
||||
#{content => "Virtual Host", width => 12},
|
||||
#{content => Host, width => 14},
|
||||
#{content => "Sessions Total", width => 18},
|
||||
#{content => Sessions, width => 8}
|
||||
],
|
||||
[
|
||||
#{content => "Accounts Total", width => 12},
|
||||
#{content => RegisteredUsers, width => 14},
|
||||
#{content => "Sessions This Node", width => 18},
|
||||
#{content => SessionsThisNode, width => 8}
|
||||
]
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Attrs, NewState}.
|
||||
|
||||
sheet_header() ->
|
||||
[
|
||||
#{title => "Username", width => 25, shortcut => "u"},
|
||||
#{title => "Resources", width => 15, shortcut => "r"}
|
||||
].
|
||||
|
||||
sheet_body(PrevState) ->
|
||||
{Host, _} = get_top_host(),
|
||||
Body = [
|
||||
begin
|
||||
[
|
||||
Username,
|
||||
length(ejabberd_sm:get_user_resources(Username, Host))
|
||||
]
|
||||
end
|
||||
|| {Username, _} <- lists:reverse(lists:sort(ejabberd_auth:get_users(Host)))
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Body, NewState}.
|
|
@ -1,78 +0,0 @@
|
|||
-module(ejabberd_observer_cli_vhosts).
|
||||
|
||||
%% observer_cli_plugin Callback API
|
||||
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||
|
||||
attributes(PrevState) ->
|
||||
Hosts = length(ejabberd_option:hosts()),
|
||||
RegisteredUsers =
|
||||
lists:foldl(
|
||||
fun(Host, Sum) ->
|
||||
ejabberd_auth:count_users(Host) + Sum
|
||||
end,
|
||||
0,
|
||||
ejabberd_option:hosts()
|
||||
),
|
||||
Sessions = length(ejabberd_sm:dirty_get_sessions_list()),
|
||||
SessionsThisNode = length(ejabberd_sm:dirty_get_my_sessions_list()),
|
||||
|
||||
OnlineRoomsNumber = lists:foldl(
|
||||
fun(Host, Acc) ->
|
||||
Acc + mod_muc:count_online_rooms(Host)
|
||||
end,
|
||||
0,
|
||||
mod_muc_admin:find_hosts(global)
|
||||
),
|
||||
|
||||
Attrs = [
|
||||
[
|
||||
#{content => "Virtual Hosts", width => 25},
|
||||
#{content => Hosts, width => 8},
|
||||
#{content => "Sessions Total", width => 25},
|
||||
#{content => Sessions, width => 8}
|
||||
],
|
||||
[
|
||||
#{content => "Accounts Total", width => 25},
|
||||
#{content => RegisteredUsers, width => 8},
|
||||
#{content => "Sessions This Node", width => 25},
|
||||
#{content => SessionsThisNode, width => 8}
|
||||
],
|
||||
[
|
||||
#{content => "MUC Rooms", width => 25},
|
||||
#{content => OnlineRoomsNumber, width => 8}
|
||||
]
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Attrs, NewState}.
|
||||
|
||||
sheet_header() ->
|
||||
[
|
||||
#{title => "Virtual Host", width => 38, shortcut => "v"},
|
||||
#{title => "Accounts", width => 11, shortcut => "a"},
|
||||
#{title => "Sessions", width => 11, shortcut => "s"},
|
||||
#{title => "Rooms", width => 8, shortcut => "r"}
|
||||
].
|
||||
|
||||
sheet_body(PrevState) ->
|
||||
Body = [
|
||||
begin
|
||||
RegisteredUsers = ejabberd_auth:count_users(Host),
|
||||
Sessions = ejabberd_sm:get_vh_session_number(Host),
|
||||
OnlineRoomsNumber = lists:foldl(
|
||||
fun(Host1, Acc) ->
|
||||
Acc + mod_muc:count_online_rooms(Host1)
|
||||
end,
|
||||
0,
|
||||
mod_muc_admin:find_hosts(Host)
|
||||
),
|
||||
[
|
||||
Host,
|
||||
RegisteredUsers,
|
||||
Sessions,
|
||||
OnlineRoomsNumber
|
||||
]
|
||||
end
|
||||
|| Host <- lists:reverse(lists:sort(ejabberd_option:hosts()))
|
||||
],
|
||||
NewState = PrevState,
|
||||
{Body, NewState}.
|
|
@ -4,8 +4,8 @@ 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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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.
|
||||
|
||||
|
@ -306,9 +306,9 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
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.
|
|
@ -0,0 +1,130 @@
|
|||
2009-09-11 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_xmlrpc.erl: Update to work with ejabberd 2.1.0
|
||||
|
||||
2009-04-28 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_xmlrpc.erl: Improve handling of errors in arguments
|
||||
|
||||
2009-04-17 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_xmlrpc.erl: Change access_commands to use the new
|
||||
AccessCommands feature of ejabberd. Syntax is similar (EJAB-910)
|
||||
|
||||
2009-03-02 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_xmlrpc.erl: Replace the listener option 'access'
|
||||
with the much more powerful 'access_commands', that allows to
|
||||
grant selective permission to execute commands with certain
|
||||
arguments
|
||||
* README.txt: Likewise
|
||||
|
||||
2009-02-24 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_xmlrpc.erl: Update to work with recent ejabberd
|
||||
trunk SVN (thanks to Artem Yurov)
|
||||
* README.txt: Likewise
|
||||
|
||||
2008-10-21 Badlop <badlop@process-one.net>
|
||||
|
||||
* README.txt: Updated PHP example
|
||||
|
||||
2008-10-12 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_xmlrpc.erl: Major rewrite of mod_xmlrpc to be an
|
||||
ejabberd independent listener, and to be a frontend of ejabberd
|
||||
commands (EJAB-694)
|
||||
|
||||
2008-08-31 Badlop <badlop@process-one.net>
|
||||
|
||||
* README.txt: Added Java example client and fixed the PHP
|
||||
example (thanks to Calder)
|
||||
|
||||
2008-07-24 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: Fixed typo in get_roster
|
||||
|
||||
2008-07-08 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: Detect if mod_roster or mod_roster_odbc is
|
||||
enabled. New call send_message (thanks to Darren Ferguson)
|
||||
* README.txt: Likewise
|
||||
|
||||
2008-05-20 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: New call get_roster; works only with
|
||||
mod_roster_odbc
|
||||
* README.txt: Likewise
|
||||
|
||||
2008-05-19 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: New function check_account (thanks to
|
||||
Zbyszek Żółkiewski)
|
||||
* README.txt: Likewise
|
||||
|
||||
2008-05-18 Badlop <badlop@process-one.net>
|
||||
|
||||
* README.txt: Added example client in PHP (thanks to Zbyszek
|
||||
Żółkiewski)
|
||||
|
||||
* src/mod_xmlrpc.erl: Convert from DOS to Unix line format
|
||||
* README.txt: Likewise
|
||||
* ChangeLog: Likewise
|
||||
|
||||
2008-05-16 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: New calls muc_room_change_option and
|
||||
muc_room_set_affiliation. Improved calls add_rosteritem and
|
||||
delete_rosteritem: they push the changed roster item to connected
|
||||
resources (thanks to Darren Ferguson). New call check_password.
|
||||
* README.txt: Likewise
|
||||
|
||||
2008-05-06 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: Added new calls delete_account,
|
||||
delete_rosteritem, create_muc_room and destroy_muc_room (thanks to
|
||||
Darren Ferguson)
|
||||
* README.txt: Likewise
|
||||
|
||||
2008-02-29 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_xmlrpc.erl: Added module options: maxsessions, timeout
|
||||
* README.txt: Likewise
|
||||
|
||||
2007-08-24 Badlop <badlop@ono.com>
|
||||
|
||||
* README.txt: Removed requirement of Xmerl 0.20, since it is
|
||||
included in Erlang/OTP. Instead, require updated XMLRPC-Erlang.
|
||||
|
||||
2007-08-21 Badlop <badlop@ono.com>
|
||||
|
||||
* README.txt: New Ruby example (thanks to Diddek). Patched xmlrpc
|
||||
Erlang library for Ruby compatibility (thanks to Cstar).
|
||||
|
||||
* mod_xmlrpc/*: Initial commit.
|
||||
|
||||
Old Changelog:
|
||||
|
||||
0.2.4 - 7 November 2006
|
||||
* Fixed a bug that required clients to provide attributes in a fixed order
|
||||
|
||||
0.2.3 - 22 September 2006
|
||||
* New feature: bind to a specific IP address, requires the patched XML-RPC-Erlang-1.13-IP (thanks to Zeank)
|
||||
|
||||
0.2.2 - 15 August 2006
|
||||
* Fixed small bug on resource_num (thanks to Flipkick)
|
||||
|
||||
0.2.1 - 20 July 2006
|
||||
* Fixed small bug on add_rosteritem (thanks to Leonexis)
|
||||
|
||||
0.2.0 - 16 April 2006
|
||||
* Added two new calls: num_resources and resouce_num
|
||||
* Added support for vhosts
|
||||
|
||||
0.1.2 - 28 December 2005
|
||||
* Now compatible with ejabberd 1.0.0
|
||||
* The XMLRPC server is started only once, not once for every virtual host
|
||||
* Added comments for handlers. Every available handler must be explained
|
||||
|
||||
0.1.0 - 4 April 2005
|
||||
* Initial version
|
|
@ -0,0 +1,2 @@
|
|||
{'../ejabberd-dev/src/gen_mod', [{outdir, "../ejabberd-dev/ebin"},{i,"../ejabberd-dev/include"}]}.
|
||||
{'src/ejabberd_xmlrpc', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
@ -0,0 +1,499 @@
|
|||
|
||||
ejabberd_xmlrpc - XML-RPC server
|
||||
|
||||
Homepage: http://www.ejabberd.im/ejabberd_xmlrpc
|
||||
Author: Badlop
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
ejabberd_xmlrpc is an ejabberd listener that starts a XML-RPC server
|
||||
and waits for external calls.
|
||||
|
||||
ejabberd_xmlrpc implements some example calls that can be used to test
|
||||
during the development of a new XML-RPC client. But most
|
||||
importantly, ejabberd_xmlrpc is also a frontend to execute ejabberd
|
||||
commands. This way a XML-RPC client can execute any ejabberd command.
|
||||
|
||||
This allows external programs written in any language like websites or
|
||||
administrative tools to communicate with ejabberd to get information
|
||||
or to make changes without the need to know ejabberd internals. One
|
||||
example usage is a corporate site in PHP that creates a Jabber user
|
||||
every time a new user is created on the website.
|
||||
|
||||
Some benefits of interfacing with the Jabber server by XML-RPC instead
|
||||
of modifying directly the database are:
|
||||
- external programs are more simple and easy to develop and debug
|
||||
- can communicate with a server in a different machine, and even on Internet
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
ejabberd 2.1.0 or higher
|
||||
XMLRPC-Erlang 1.13 with IP, Ruby and Xmerl 1.x patches
|
||||
Optional: mod_admin_extra implements many ejabberd commands for general server administration
|
||||
Optional: mod_muc_admin implements ejabberd commands for MUC administration
|
||||
|
||||
|
||||
CONFIGURE EJABBERD
|
||||
------------------
|
||||
|
||||
1. You need to get and install XMLRPC-Erlang.
|
||||
You can download XMLRPC-Erlang binary files from
|
||||
http://www.ejabberd.im/ejabberd_xmlrpc
|
||||
or compile it yourself:
|
||||
wget http://www.ejabberd.im/files/contributions/xmlrpc-1.13-ipr2.tgz
|
||||
tar -xzvf xmlrpc-1.13-ipr2.tgz
|
||||
cd xmlrpc-1.13/src
|
||||
make
|
||||
cd ../../
|
||||
Then you can copy the *.beam files to ejabberd ebin directory,
|
||||
or add an option like this to the ejabberd start script:
|
||||
$ erl -pa '/home/jabber/xmlrpc-1.13/ebin' ...
|
||||
|
||||
2. Configure ejabberd to start this listener at startup:
|
||||
edit ejabberd.cfg and add on the 'listen' section:
|
||||
{listen, [
|
||||
{4560, ejabberd_xmlrpc, []},
|
||||
...
|
||||
]}.
|
||||
|
||||
3. Start ejabberd.
|
||||
|
||||
4. Verify that ejabberd is listening in that port:
|
||||
$ netstat -n -l | grep 4560
|
||||
tcp 0 0 0.0.0.0:4560 0.0.0.0:* LISTEN
|
||||
|
||||
5. If there is any problem, check ejabberd.log and sasl.log files
|
||||
|
||||
|
||||
CONFIGURE
|
||||
---------
|
||||
|
||||
The listener allow several configurable options:
|
||||
|
||||
{maxsessions, Integer}
|
||||
Number of concurrent connections allowed.
|
||||
Default: 10
|
||||
|
||||
{timeout, Integer}
|
||||
Timeout of the connections, expressed in milliseconds.
|
||||
Default: 5000
|
||||
|
||||
{access_commands, AccessCommands}
|
||||
This option allows to define a list of access restrictions.
|
||||
If this option is present, then XML-RPC calls must include as
|
||||
first argument a struct with a user, server and password of an
|
||||
account in ejabberd that has privileges in Access.
|
||||
If the option is not present, such struct must not be provided.
|
||||
The default value is to not define any restriction: []
|
||||
When one or several access restrictions are defined and the
|
||||
XML-RPC call provides authentication for an account, each
|
||||
restriction is verified until one matches completely:
|
||||
the account matches the Access rule,
|
||||
the command name is listed in CommandNames,
|
||||
and the provided arguments do not contradict Arguments.
|
||||
There is more information about AccessCommands in the ejabberd Guide.
|
||||
|
||||
|
||||
Example configuration: XML-RPC calls can execute any command, with any
|
||||
argument, and no authentication information must be provided:
|
||||
{listen, [
|
||||
{4560, ejabberd_xmlrpc, [{maxsessions, 10}, {timeout, 5000}]},
|
||||
...
|
||||
]}.
|
||||
|
||||
In this case authentication information must be provided, but it is
|
||||
enough that the account exists and the password is valid to execute
|
||||
any command:
|
||||
{listen, [
|
||||
{4560, ejabberd_xmlrpc, [{maxsessions, 10}, {timeout, 5000},
|
||||
{access_commands, [{all, all, []}]}]},
|
||||
...
|
||||
]}.
|
||||
|
||||
In this example the local Jabber account xmlrpc-robot@jabber.example.org
|
||||
can execute any command with no argument restriction:
|
||||
{acl, xmlrpcbot, {user, "xmlrpc-robot", "jabber.example.org"}}.
|
||||
{access, xmlrpcaccess, [{allow, xmlrpcbot}]}.
|
||||
{listen, [
|
||||
{4560, ejabberd_xmlrpc, [{maxsessions, 10}, {timeout, 5000},
|
||||
{access_commands, [{xmlrpcaccess, all, []}]}]},
|
||||
...
|
||||
]}.
|
||||
|
||||
Finally, in this complex example the listener only listens in port
|
||||
4560 of IP address 127.0.0.1, and several access restrictions are
|
||||
defined (the corresponding ACL and ACCESS are not shown):
|
||||
{listen, [
|
||||
{{4560, "127.0.0.1"}, ejabberd_xmlrpc, [
|
||||
{access_commands, [
|
||||
%% This bot can execute any command:
|
||||
{xmlrpc_bot, all, []},
|
||||
%% This bot can execute any command,
|
||||
%% but if a 'host' argument is provided, it must be "example.org":
|
||||
{xmlrpc_bot_all_example, all, [{host, "example.org"}]},
|
||||
%% This bot can only execute the command 'dump'. No argument restriction:
|
||||
{xmlrpc_bot_backups, [dump], []}
|
||||
%% This bot can only execute the command 'register',
|
||||
%% and if argument 'host' is provided, it must be "example.org":
|
||||
{xmlrpc_bot_reg_example, [register], [{host, "example.org"}]},
|
||||
%% This bot can execute the commands 'register' and 'unregister',
|
||||
%% if argument host is provided, it must be "test.org":
|
||||
{xmlrpc_bot_reg_test, [register, unregister], [{host, "test.org"}]}
|
||||
]}
|
||||
]},
|
||||
...
|
||||
]}.
|
||||
|
||||
|
||||
|
||||
USAGE
|
||||
-----
|
||||
|
||||
You can send calls to http://host:4560/
|
||||
|
||||
Call: Arguments: Returns:
|
||||
|
||||
-- debug
|
||||
echothis String String
|
||||
echothisnew struct[{sentence, String}] struct[{repeated, String}]
|
||||
multhis struct[{a, Integer}, {b, Integer}] Integer
|
||||
multhisnew struct[{a, Integer}, {b, Integer}] struct[{mu, Integer}]
|
||||
|
||||
-- statistics
|
||||
tellme_title String String
|
||||
tellme_value String String
|
||||
tellme String struct[{title, String}, {value. String}]
|
||||
|
||||
|
||||
With ejabberd_xmlrpc you can execute any ejabberd command with a XML-RPC call.
|
||||
|
||||
1. Get a list of available ejabberd commands, for example:
|
||||
$ ejabberdctl help
|
||||
Available commands in this ejabberd node:
|
||||
connected_users List all established sessions
|
||||
connected_users_number Get the number of established sessions
|
||||
delete_expired_messages Delete expired offline messages from database
|
||||
delete_old_messages days Delete offline messages older than DAYS
|
||||
dump file Dump the database to text file
|
||||
register user host password Register a user
|
||||
registered_users host List all registered users in HOST
|
||||
reopen_log Reopen the log files
|
||||
restart Restart ejabberd
|
||||
restore file Restore the database from backup file
|
||||
status Get ejabberd status
|
||||
stop Stop ejabberd
|
||||
unregister user host Unregister a user
|
||||
user_resources user host List user's connected resources
|
||||
|
||||
2. When you found the command you want to call, get some additional
|
||||
help of the arguments and result:
|
||||
$ ejabberdctl help user_resources
|
||||
Command Name: user_resources
|
||||
Arguments: user::string
|
||||
host::string
|
||||
Returns: resources::[ resource::string ]
|
||||
Tags: session
|
||||
Description: List user's connected resources
|
||||
|
||||
3. You can try to execute the command in the shell for the account testuser@localhost:
|
||||
$ ejabberdctl user_resources testuser localhost
|
||||
Home
|
||||
Psi
|
||||
|
||||
4. Now implement the proper XML-RPC call in your XML-RPC client.
|
||||
This example will use the Erlang library:
|
||||
$ erl
|
||||
1> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, user_resources, [{struct, [{user, "testuser"}, {host, "localhost"}]}]}).
|
||||
{ok,{response,[{struct,[{resources,{array,[{struct,[{resource,"Home"}]},
|
||||
{struct,[{resource,"Psi"}]}]}}]}]}}
|
||||
|
||||
5. Note: if ejabberd_xmlrpc has the option 'access_commands'
|
||||
configured with some access restriction (see the example
|
||||
configurations provided above), the XML-RPC must include first an
|
||||
argument providing information of a valid account. For example:
|
||||
1> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, user_resources, [
|
||||
{struct, [{user, "adminuser"}, {server, "localhost"}, {password, "aeiou"}]},
|
||||
{struct, [{user, "testuser"}, {host, "localhost"}]} ]}).
|
||||
|
||||
|
||||
Arguments in XML-RPC calls can be provided in any order;
|
||||
This module will sort the arguments before calling the ejabberd command.
|
||||
|
||||
If auth is provided in the call when ejabberd_xmlrpc does not require it,
|
||||
the call will return the error: -112 Unknown call
|
||||
|
||||
|
||||
|
||||
EXAMPLE IN PHP
|
||||
--------------
|
||||
|
||||
This is an XML-RPC client in PHP, thanks to Zbyszek Żółkiewski and Calder.
|
||||
It requires "allow_url_fopen = On" in your php.ini.
|
||||
|
||||
-------
|
||||
<?
|
||||
$param=array("user"=>"testuser", "host"=>"localhost");
|
||||
$request = xmlrpc_encode_request('user_resources', $param, (array('encoding' => 'utf-8')));
|
||||
|
||||
$context = stream_context_create(array('http' => array(
|
||||
'method' => "POST",
|
||||
'header' => "User-Agent: XMLRPC::Client mod_xmlrpc\r\n" .
|
||||
"Content-Type: text/xml\r\n" .
|
||||
"Content-Length: ".strlen($request),
|
||||
'content' => $request
|
||||
)));
|
||||
|
||||
$file = file_get_contents("http://127.0.0.1:4560/RPC2", false, $context);
|
||||
|
||||
$response = xmlrpc_decode($file);
|
||||
|
||||
if (xmlrpc_is_fault($response)) {
|
||||
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
|
||||
} else {
|
||||
print_r($response);
|
||||
}
|
||||
|
||||
?>
|
||||
-------
|
||||
|
||||
The response, following the example would be like this:
|
||||
-------
|
||||
$ php5 call.php
|
||||
Array
|
||||
(
|
||||
[resources] => Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
[resource] => Home
|
||||
)
|
||||
[1] => Array
|
||||
(
|
||||
[resource] => Psi
|
||||
)
|
||||
)
|
||||
)
|
||||
-------
|
||||
|
||||
If you configured the option access_commands, you have to provide authentication
|
||||
information by replacing the first lime with something like this:
|
||||
-------
|
||||
$param_auth=array("user"=>"analloweduser", "server"=>"localhost", "password"=>"MyPasS997");
|
||||
$param_comm=array("user"=>"testuser", "host"=>"localhost");
|
||||
$param=array($param_auth, $param_comm);
|
||||
-------
|
||||
|
||||
|
||||
**** WARNING: all the remaining text was written for mod_xmlrpc and
|
||||
is NOT valid for ejabberd_xmlrpc ****
|
||||
|
||||
|
||||
TEST
|
||||
----
|
||||
|
||||
- You can easily try the XML-RPC server starting a new Erlang Virtual Machine
|
||||
and making calls to ejabberd's XML-RPC:
|
||||
|
||||
1. Start Erlang with this option:
|
||||
$ erl -pa '/home/jabber/xmlrpc-1.13/ebin'
|
||||
|
||||
2. Now on the Erlang console, write commands and check the results:
|
||||
|
||||
1> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [800]}).
|
||||
{ok,{response,[800]}}
|
||||
|
||||
2> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, ["blot cloc 557.889 kg"]}).
|
||||
{ok,{response,["blot cloc 557.889 kg"]}}
|
||||
|
||||
3> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, multhis, [{struct,[{a, 83}, {b, 689}]}]}).
|
||||
{ok,{response,[57187]}}
|
||||
|
||||
4> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, register,
|
||||
[{struct, [{user, "ggeo"}, {host, "example.com"}, {password, "gogo11"}]}]}).
|
||||
{ok,{response,[0]}}
|
||||
|
||||
5> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, register,
|
||||
[{struct, [{user, "ggeo"}, {host, "example.com"}, {password, "gogo11"}]}]}).
|
||||
{ok,{response,[409]}}
|
||||
|
||||
6> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, muc_room_change_option,
|
||||
[{struct, [{name, "test"}, {service, "conference.localhost"},
|
||||
{option, "title"}, {value, "Test Room"}]}]}).
|
||||
|
||||
7> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, muc_room_set_affiliation,
|
||||
[{struct, [{name, "test"}, {service, "conference.example.com"},
|
||||
{jid, "ex@example.com"}, {affiliation, "member"}]}]}).
|
||||
|
||||
8> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, muc_room_set_affiliation,
|
||||
[{struct, [{name, "test"}, {service, "conference.example.com"},
|
||||
{jid, "ex@example.com"}, {affiliation, "none"}]}]}).
|
||||
|
||||
|
||||
- Some possible XML-RPC error messages:
|
||||
|
||||
+ Client: connection refused: wrong IP, wrong port, the server is not started...
|
||||
|
||||
2> xmlrpc:call({127, 0, 0, 1}, 44444, "/", {call, echothis, [800]}).
|
||||
{error,econnrefused}
|
||||
|
||||
+ Client: bad value: a800 is a string, so it must be put into ""
|
||||
|
||||
7> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [a800]}).
|
||||
{error,{bad_value,a800}}
|
||||
|
||||
+ Server: unknown call: you sent a call that the server does not implement
|
||||
|
||||
3> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, bububu, [800]}).
|
||||
{ok,{response,{fault,-1,"Unknown call: {call,bububu,[800]}"}}}
|
||||
|
||||
|
||||
EXAMPLE IN PYTHON
|
||||
-----------------
|
||||
|
||||
This is an example XML-RPC client in Python, thanks to Diddek:
|
||||
-------
|
||||
import xmlrpclib
|
||||
|
||||
server_url = 'http://127.0.0.1:4560';
|
||||
server = xmlrpclib.Server(server_url);
|
||||
|
||||
params = {}
|
||||
params["user"] = "ggeo"
|
||||
params["host"] = "localhost"
|
||||
params["password"] = "gogo11"
|
||||
|
||||
result = server.register(params)
|
||||
print result
|
||||
-------
|
||||
|
||||
This Python example shows how to provide authentication in the call, thanks to Muelli:
|
||||
-------
|
||||
import xmlrpclib
|
||||
|
||||
server_url = 'http://127.0.0.1:4560'
|
||||
server = xmlrpclib.ServerProxy(server_url)
|
||||
|
||||
EJABBERD_XMLRPC_LOGIN = {'user': 'adminuser', 'server': 'localhost', 'password': 'aeiou'}
|
||||
|
||||
def ejabberdctl(command, data):
|
||||
fn = getattr(server, command)
|
||||
return fn(EJABBERD_XMLRPC_LOGIN, data)
|
||||
|
||||
result = ejabberdctl('register', {'user':'ggeo', 'host':'localhost', 'password':'gogo11'})
|
||||
print result
|
||||
-------
|
||||
|
||||
|
||||
EXAMPLE IN RUBY
|
||||
---------------
|
||||
|
||||
This is an example XML-RPC client in Ruby, thanks to Diddek:
|
||||
-------
|
||||
require 'xmlrpc/client'
|
||||
|
||||
host = "172.16.29.6:4560"
|
||||
timeout = 3000000
|
||||
client = XMLRPC::Client.new2("http://#{host}", "#{host}", timeout)
|
||||
result = client.call("echothis", "800")
|
||||
puts result
|
||||
-------
|
||||
|
||||
|
||||
EXAMPLE IN JAVA
|
||||
---------------
|
||||
|
||||
This is an XML-RPC client in Java, thanks to Calder.
|
||||
It requires Apache XML-RPC available at http://ws.apache.org/xmlrpc/
|
||||
|
||||
-------
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||
|
||||
public class Test {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
|
||||
config.setServerURL(new URL("http://127.0.0.1:4560/RPC2"));
|
||||
XmlRpcClient client = new XmlRpcClient();
|
||||
client.setConfig(config);
|
||||
|
||||
/* Command string */
|
||||
String command = "check_password";
|
||||
|
||||
/* Parameters as struct */
|
||||
Map struct = new HashMap();
|
||||
struct.put("user", "test1");
|
||||
struct.put("host", "localhost");
|
||||
struct.put("password", "test");
|
||||
|
||||
Object[] params = new Object[]{struct};
|
||||
Integer result = (Integer) client.execute(command, params);
|
||||
System.out.println(result);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-------
|
||||
|
||||
|
||||
EXAMPLE IN C#
|
||||
-------------
|
||||
|
||||
This is an XML-RPC client in C#, thanks to Mitchel Constantin.
|
||||
|
||||
-------
|
||||
// Copyright: 2010 Weavver, Inc.
|
||||
// Author: Mitchel Constantin <mythicalbox@weavver.com>
|
||||
// License: Public Domain (Limited to this file)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CookComputing.XmlRpc;
|
||||
|
||||
namespace Weavver.Vendors.ProcessOne
|
||||
{
|
||||
public struct send_message_chat
|
||||
{
|
||||
public string from;
|
||||
public string to;
|
||||
public string body;
|
||||
}
|
||||
public struct status {}
|
||||
public class ejabberdRPC
|
||||
{
|
||||
[XmlRpcUrl("http://205.134.225.18:4560/RPC2")]
|
||||
public interface IStateName : IXmlRpcProxy
|
||||
{
|
||||
[XmlRpcMethod("send_message_chat")]
|
||||
object SendMessageChat(send_message_chat message);
|
||||
[XmlRpcMethod("status")]
|
||||
object Status(status s);
|
||||
}
|
||||
public CookComputing.XmlRpc.XmlRpcStruct SendMessageChat(send_message_chat message)
|
||||
{
|
||||
IStateName proxy = XmlRpcProxyGen.Create<IStateName>();
|
||||
proxy.KeepAlive = false;
|
||||
return (CookComputing.XmlRpc.XmlRpcStruct) proxy.SendMessageChat(message);
|
||||
}
|
||||
public CookComputing.XmlRpc.XmlRpcStruct Status(status status)
|
||||
{
|
||||
IStateName proxy = XmlRpcProxyGen.Create<IStateName>();
|
||||
proxy.KeepAlive = false;
|
||||
return (CookComputing.XmlRpc.XmlRpcStruct) proxy.Status(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
-------
|
|
@ -0,0 +1 @@
|
|||
erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
erl -pa ../ejabberd-dev/ebin -pz ebin -make
|
|
@ -0,0 +1,467 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_xmlrpc.erl
|
||||
%%% Author : Badlop <badlop@process-one.net>
|
||||
%%% Purpose : XML-RPC server that frontends ejabberd commands
|
||||
%%% Created : 21 Aug 2007 by Badlop <badlop@ono.com>
|
||||
%%% Id : $Id: ejabberd_xmlrpc.erl 595 2008-05-20 11:39:31Z badlop $
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%% TODO: Implement a command in ejabberdctl 'help COMMAND LANGUAGE' that shows
|
||||
%%% a coding example to call that command in a specific language (python, php).
|
||||
|
||||
%%% TODO: Remove support for plaintext password
|
||||
|
||||
%%% TODO: commands strings should be strings without ~n
|
||||
|
||||
-module(ejabberd_xmlrpc).
|
||||
-author('badlop@process-one.net').
|
||||
|
||||
-export([
|
||||
start_listener/2,
|
||||
handler/2,
|
||||
socket_type/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {access_commands, auth = noauth, get_auth}).
|
||||
|
||||
|
||||
%% Test:
|
||||
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_integer, [{struct, [{thisinteger, 5}]}]}).
|
||||
%% {ok,{response,[{struct,[{zero,0}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_string, [{struct, [{thisstring, "abcd"}]}]}).
|
||||
%% {ok,{response,[{struct,[{thatstring,"abcd"}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, tell_tuple_3integer, [{struct, [{thisstring, "abcd"}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{thattuple,
|
||||
%% {array,
|
||||
%% [{struct,[{first,123}]},
|
||||
%% {struct,[{second,456}]},
|
||||
%% {struct,[{third,789}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, pow, [{struct, [{base, 5}, {exponent, 7}]}]}).
|
||||
%% {ok,{response,[{struct,[{pow,78125}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, seq, [{struct, [{from, 3}, {to, 7}]}]}).
|
||||
%% {ok,{response,[{array,[{struct,[{intermediate,3}]},
|
||||
%% {struct,[{intermediate,4}]},
|
||||
%% {struct,[{intermediate,5}]},
|
||||
%% {struct,[{intermediate,6}]},
|
||||
%% {struct,[{intermediate,7}]}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, substrs, [{struct, [{word, "abcd"}]}]}).
|
||||
%% NO:
|
||||
%% {ok,{response,[{array,[{struct,[{miniword,"a"}]},
|
||||
%% {struct,[{miniword,"ab"}]},
|
||||
%% {struct,[{miniword,"abc"}]},
|
||||
%% {struct,[{miniword,"abcd"}]}]}]}}
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{substrings,
|
||||
%% {array,
|
||||
%% [{struct,[{miniword,"a"}]},
|
||||
%% {struct,[{miniword,"ab"}]},
|
||||
%% {struct,[{miniword,"abc"}]},
|
||||
%% {struct,[{miniword,"abcd"}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, splitjid, [{struct, [{jid, "abcd@localhost/work"}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{jidparts,
|
||||
%% {array,
|
||||
%% [{struct,[{user,"abcd"}]},
|
||||
%% {struct,[{server,"localhost"}]},
|
||||
%% {struct,[{resource,"work"}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 55}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{thistuple,
|
||||
%% {array,
|
||||
%% [{struct,[{thisinteger,55}]},
|
||||
%% {struct,[{thisstring,"abc"}]}]}}]}]}}
|
||||
%%
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_list_integer, [{struct, [{thislist, {array, [{struct, [{thisinteger, 55}, {thisinteger, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{thatlist,
|
||||
%% {array,
|
||||
%% [{struct,[{thatinteger,55}]},
|
||||
%% {struct,[{thatinteger,4567}]}]}}]}]}}
|
||||
%%
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_list_string, [{struct, [{thisinteger, 123456}, {thislist, {array, [{struct, [{thisstring, "abc"}, {thisstring, "bobo baba"}]}]}}]}]}).
|
||||
%% {ok,
|
||||
%% {response,
|
||||
%% [{struct,
|
||||
%% [{thistuple,
|
||||
%% {array,
|
||||
%% [{struct,[{thatinteger,123456}]},
|
||||
%% {struct,
|
||||
%% [{thatlist,
|
||||
%% {array,
|
||||
%% [{struct,[{thatstring,"abc"}]},
|
||||
%% {struct,[{thatstring,"bobo baba"}]}]}}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger2, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,[{struct,[{zero,0}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_isatils, [{struct,
|
||||
%% [{thisinteger, 123456990},
|
||||
%% {thisstring, "This is ISATILS"},
|
||||
%% {thisatom, "test_isatils"},
|
||||
%% {thistuple, {array, [{struct, [
|
||||
%% {listlen, 2},
|
||||
%% {thislist, {array, [{struct, [
|
||||
%% {contentstring, "word1"},
|
||||
%% {contentstring, "word 2"}
|
||||
%% ]}]}}
|
||||
%% ]}]}}
|
||||
%% ]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{results,
|
||||
%% {array,
|
||||
%% [{struct,[{thatinteger,123456990}]},
|
||||
%% {struct,[{thatstring,"This is ISATILS"}]},
|
||||
%% {struct,[{thatatom,"test_isatils"}]},
|
||||
%% {struct,
|
||||
%% [{thattuple,
|
||||
%% {array,
|
||||
%% [{struct,[{listlen,123456990}]},
|
||||
%% {struct,[{thatlist,...}]}]}}]}]}}]}]}}
|
||||
|
||||
%% ecommand doesn't exist:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string2, [{struct, [{thisstring, "abc"}]}]}).
|
||||
%% {ok,{response,{fault,-1, "Unknown call: {call,echo_integer_string2,[{struct,[{thisstring,\"abc\"}]}]}"}}}
|
||||
%%
|
||||
%% Duplicated argument:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 44}, {thisinteger, 55}]}]}).
|
||||
%% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger' duplicated:\n[{thisstring,\"abc\"},{thisinteger,44},{thisinteger,55}]"}}}
|
||||
%%
|
||||
%% Missing argument:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}]}]}).
|
||||
%% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger' not found:\n[{thisstring,\"abc\"}]"}}}
|
||||
%%
|
||||
%% Duplicated tuple element:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger1, 66}, {thisinteger2, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger1' defined multiple times:\n[{thisinteger1,55},{thisinteger1,66},{thisinteger2,4567}]"}}}
|
||||
%%
|
||||
%% Missing element in tuple:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisintegerc, 66}, {thisinteger, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger2' not found:\n[{thisintegerc,66},{thisinteger,4567}]"}}}
|
||||
%%
|
||||
%% The ecommand crashed:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, this_crashes, [{struct, []}]}).
|
||||
%% {ok,{response,{fault,-100, "Error -100\nA problem 'error' occurred executing the command this_crashes with arguments []: badarith"}}}
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Listener interface
|
||||
%% -----------------------------
|
||||
|
||||
start_listener({Port, Ip, tcp = _TranportProtocol}, Opts) ->
|
||||
%% get options
|
||||
MaxSessions = gen_mod:get_opt(maxsessions, Opts, 10),
|
||||
Timeout = gen_mod:get_opt(timeout, Opts, 5000),
|
||||
AccessCommands = gen_mod:get_opt(access_commands, Opts, []),
|
||||
GetAuth = case [ACom || {Ac, _, _} = ACom <- AccessCommands, Ac /= all] of
|
||||
[] -> false;
|
||||
_ -> true
|
||||
end,
|
||||
|
||||
%% start the XML-RPC server
|
||||
Handler = {?MODULE, handler},
|
||||
State = #state{access_commands = AccessCommands, get_auth = GetAuth},
|
||||
xmlrpc:start_link(Ip, Port, MaxSessions, Timeout, Handler, State).
|
||||
|
||||
socket_type() ->
|
||||
independent.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Access verification
|
||||
%% -----------------------------
|
||||
|
||||
%% @spec (AuthList) -> {User, Server, Password}
|
||||
%% where
|
||||
%% AuthList = [{user, string()}, {server, string()}, {password, string()}]
|
||||
%% It may throw: {error, missing_auth_arguments, Attr}
|
||||
get_auth(AuthList) ->
|
||||
%% Check AuthList contains all the required data
|
||||
[User, Server, Password] =
|
||||
try get_attrs([user, server, password], AuthList) of
|
||||
[U, S, P] -> [U, S, P]
|
||||
catch
|
||||
exit:{attribute_not_found, Attr, _} ->
|
||||
throw({error, missing_auth_arguments, Attr})
|
||||
end,
|
||||
{User, Server, Password}.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Handlers
|
||||
%% -----------------------------
|
||||
|
||||
%% Call: Arguments: Returns:
|
||||
|
||||
|
||||
%% .............................
|
||||
%% Access verification
|
||||
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [152]}).
|
||||
%% {ok,{response,{fault,-103, "Error -103\nRequired authentication: {call,echothis,[152]}"}}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada"}]}, 152]}).
|
||||
%% {ok,{response,{fault,-103,
|
||||
%% "Error -103\nAuthentication non valid: [{user,\"badlop\"},\n
|
||||
%% {server,\"localhost\"},\n
|
||||
%% {password,\"ada\"}]"}}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada90ada"}]}, 152]}).
|
||||
%% {ok,{response,[152]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "79C1574A43BC995F2B145A299EF97277"}]}, 152]}).
|
||||
%% {ok,{response,[152]}}
|
||||
|
||||
handler(#state{get_auth = true, auth = noauth} = State, {call, Method, [{struct, AuthList} | Arguments] = AllArgs}) ->
|
||||
try get_auth(AuthList) of
|
||||
Auth ->
|
||||
handler(State#state{get_auth = false, auth = Auth}, {call, Method, Arguments})
|
||||
catch
|
||||
{error, missing_auth_arguments, _Attr} ->
|
||||
handler(State#state{get_auth = false, auth = noauth}, {call, Method, AllArgs})
|
||||
end;
|
||||
|
||||
|
||||
%% .............................
|
||||
%% Debug
|
||||
|
||||
%% echothis String String
|
||||
handler(_State, {call, echothis, [A]}) ->
|
||||
{false, {response, [A]}};
|
||||
|
||||
%% echothisnew struct[{sentence, String}] struct[{repeated, String}]
|
||||
handler(_State, {call, echothisnew, [{struct, [{sentence, A}]}]}) ->
|
||||
{false, {response, [{struct, [{repeated, A}]}]}};
|
||||
|
||||
%% multhis struct[{a, Integer}, {b, Integer}] Integer
|
||||
handler(_State, {call, multhis, [{struct, [{a, A}, {b, B}]}]}) ->
|
||||
{false, {response, [A*B]}};
|
||||
|
||||
%% multhisnew struct[{a, Integer}, {b, Integer}] struct[{mu, Integer}]
|
||||
handler(_State, {call, multhisnew, [{struct, [{a, A}, {b, B}]}]}) ->
|
||||
{false, {response, [{struct, [{mu, A*B}]}]}};
|
||||
|
||||
%% .............................
|
||||
%% Statistics
|
||||
|
||||
%% tellme_title String String
|
||||
handler(_State, {call, tellme_title, [A]}) ->
|
||||
{false, {response, [get_title(A)]}};
|
||||
|
||||
%% tellme_value String String
|
||||
handler(_State, {call, tellme_value, [A]}) ->
|
||||
N = node(),
|
||||
{false, {response, [get_value(N, A)]}};
|
||||
|
||||
%% tellme String struct[{title, String}, {value. String}]
|
||||
handler(_State, {call, tellme, [A]}) ->
|
||||
N = node(),
|
||||
T = {title, get_title(A)},
|
||||
V = {value, get_value(N, A)},
|
||||
R = {struct, [T, V]},
|
||||
{false, {response, [R]}};
|
||||
|
||||
%% .............................
|
||||
%% ejabberd commands
|
||||
|
||||
handler(State, {call, Command, []}) ->
|
||||
%% The XMLRPC request may not contain a struct parameter,
|
||||
%% but our internal functions need such struct, even if it's empty
|
||||
%% So let's add it and do a recursive call:
|
||||
handler(State, {call, Command, [{struct, []}]});
|
||||
|
||||
handler(State, {call, Command, [{struct, AttrL}]} = Payload) ->
|
||||
case ejabberd_commands:get_command_format(Command) of
|
||||
{error, command_unknown} ->
|
||||
build_fault_response(-112, "Unknown call: ~p", [Payload]);
|
||||
{ArgsF, ResultF} ->
|
||||
try_do_command(State#state.access_commands, State#state.auth, Command, AttrL, ArgsF, ResultF)
|
||||
end;
|
||||
|
||||
%% If no other guard matches
|
||||
handler(_State, Payload) ->
|
||||
build_fault_response(-112, "Unknown call: ~p", [Payload]).
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Command
|
||||
%% -----------------------------
|
||||
|
||||
try_do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) ->
|
||||
try do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) of
|
||||
{command_result, ResultFormatted} ->
|
||||
{false, {response, [ResultFormatted]}}
|
||||
catch
|
||||
exit:{duplicated_attribute, ExitAt, ExitAtL} ->
|
||||
build_fault_response(-114, "Attribute '~p' duplicated:~n~p", [ExitAt, ExitAtL]);
|
||||
exit:{attribute_not_found, ExitAt, ExitAtL} ->
|
||||
build_fault_response(-116, "Required attribute '~p' not found:~n~p", [ExitAt, ExitAtL]);
|
||||
exit:{additional_unused_args, ExitAtL} ->
|
||||
build_fault_response(-120, "The call provided additional unused arguments:~n~p", [ExitAtL]);
|
||||
throw:Why ->
|
||||
build_fault_response(-118, "A problem '~p' occurred executing the command ~p with arguments~n~p", [Why, Command, AttrL])
|
||||
end.
|
||||
|
||||
build_fault_response(Code, ParseString, ParseArgs) ->
|
||||
FaultString = "Error " ++ integer_to_list(Code) ++ "\n" ++
|
||||
lists:flatten(io_lib:format(ParseString, ParseArgs)),
|
||||
?WARNING_MSG(FaultString, []), %% Show Warning message in ejabberd log file
|
||||
{false, {response, {fault, Code, FaultString}}}.
|
||||
|
||||
do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) ->
|
||||
ArgsFormatted = format_args(AttrL, ArgsF),
|
||||
Result = ejabberd_commands:execute_command(AccessCommands, Auth, Command, ArgsFormatted),
|
||||
ResultFormatted = format_result(Result, ResultF),
|
||||
{command_result, ResultFormatted}.
|
||||
|
||||
|
||||
%%-----------------------------
|
||||
%% Format arguments
|
||||
%%-----------------------------
|
||||
|
||||
get_attrs(Attribute_names, L) ->
|
||||
[get_attr(A, L) || A <- Attribute_names].
|
||||
|
||||
get_attr(A, L) ->
|
||||
case lists:keysearch(A, 1, L) of
|
||||
{value, {A, Value}} -> Value;
|
||||
false ->
|
||||
%% Report the error and then force a crash
|
||||
exit({attribute_not_found, A, L})
|
||||
end.
|
||||
|
||||
%% Get an element from a list and delete it.
|
||||
%% The element must be defined once and only once,
|
||||
%% otherwise the function crashes on purpose.
|
||||
get_elem_delete(A, L) ->
|
||||
case proplists:get_all_values(A, L) of
|
||||
[Value] ->
|
||||
{Value, proplists:delete(A, L)};
|
||||
[_, _ | _] ->
|
||||
%% Crash reporting the error
|
||||
exit({duplicated_attribute, A, L});
|
||||
[] ->
|
||||
%% Report the error and then force a crash
|
||||
exit({attribute_not_found, A, L})
|
||||
end.
|
||||
|
||||
|
||||
format_args(Args, ArgsFormat) ->
|
||||
{ArgsRemaining, R} =
|
||||
lists:foldl(
|
||||
fun({ArgName, ArgFormat}, {Args1, Res}) ->
|
||||
{ArgValue, Args2} = get_elem_delete(ArgName, Args1),
|
||||
Formatted = format_arg(ArgValue, ArgFormat),
|
||||
{Args2, Res ++ [Formatted]}
|
||||
end,
|
||||
{Args, []},
|
||||
ArgsFormat),
|
||||
case ArgsRemaining of
|
||||
[] -> R;
|
||||
L when is_list(L) ->
|
||||
exit({additional_unused_args, L})
|
||||
end.
|
||||
format_arg({array, [{struct, Elements}]}, {list, {ElementDefName, ElementDefFormat}})
|
||||
when is_list(Elements) ->
|
||||
lists:map(
|
||||
fun({ElementName, ElementValue}) ->
|
||||
true = (ElementDefName == ElementName),
|
||||
format_arg(ElementValue, ElementDefFormat)
|
||||
end,
|
||||
Elements);
|
||||
format_arg({array, [{struct, Elements}]}, {tuple, ElementsDef})
|
||||
when is_list(Elements) ->
|
||||
FormattedList = format_args(Elements, ElementsDef),
|
||||
list_to_tuple(FormattedList);
|
||||
format_arg({array, Elements}, {list, ElementsDef})
|
||||
when is_list(Elements) and is_atom(ElementsDef) ->
|
||||
[format_arg(Element, ElementsDef) || Element <- Elements];
|
||||
format_arg(Arg, integer)
|
||||
when is_integer(Arg) ->
|
||||
Arg;
|
||||
format_arg(Arg, string)
|
||||
when is_list(Arg) ->
|
||||
Arg.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Result
|
||||
%% -----------------------------
|
||||
|
||||
format_result({error, Error}, _) ->
|
||||
throw({error, Error});
|
||||
|
||||
format_result(String, string) ->
|
||||
lists:flatten(String);
|
||||
|
||||
format_result(Atom, {Name, atom}) ->
|
||||
{struct, [{Name, atom_to_list(Atom)}]};
|
||||
|
||||
format_result(Int, {Name, integer}) ->
|
||||
{struct, [{Name, Int}]};
|
||||
|
||||
format_result(String, {Name, string}) ->
|
||||
{struct, [{Name, lists:flatten(String)}]};
|
||||
|
||||
format_result(Code, {Name, rescode}) ->
|
||||
{struct, [{Name, make_status(Code)}]};
|
||||
|
||||
format_result({Code, Text}, {Name, restuple}) ->
|
||||
{struct, [{Name, make_status(Code)},
|
||||
{text, lists:flatten(Text)}]};
|
||||
|
||||
%% Result is a list of something: [something()]
|
||||
format_result(Elements, {Name, {list, ElementsDef}}) ->
|
||||
FormattedList = lists:map(
|
||||
fun(Element) ->
|
||||
format_result(Element, ElementsDef)
|
||||
end,
|
||||
Elements),
|
||||
{struct, [{Name, {array, FormattedList}}]};
|
||||
|
||||
%% Result is a tuple with several elements: {something1(), something2(), ...}
|
||||
format_result(ElementsTuple, {Name, {tuple, ElementsDef}}) ->
|
||||
ElementsList = tuple_to_list(ElementsTuple),
|
||||
ElementsAndDef = lists:zip(ElementsList, ElementsDef),
|
||||
FormattedList = lists:map(
|
||||
fun({Element, ElementDef}) ->
|
||||
format_result(Element, ElementDef)
|
||||
end,
|
||||
ElementsAndDef),
|
||||
{struct, [{Name, {array, FormattedList}}]}.
|
||||
%% TODO: should be struct instead of array?
|
||||
|
||||
|
||||
make_status(ok) -> 0;
|
||||
make_status(true) -> 0;
|
||||
make_status(false) -> 1;
|
||||
make_status(error) -> 1;
|
||||
make_status(_) -> 1.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Internal
|
||||
%% -----------------------------
|
||||
|
||||
get_title(A) -> mod_statsdx:get_title(A).
|
||||
get_value(N, A) -> mod_statsdx:get(N, [A]).
|
|
@ -1,5 +0,0 @@
|
|||
author: "Gregor Uhlenheuer <kongo2002 at gmail.com>"
|
||||
category: "archive"
|
||||
summary: "Message Archive Management (XEP-0313)"
|
||||
home: "https://github.com/kongo2002/ejabberd-mod-mam/tree/master/"
|
||||
url: "git@github.com:kongo2002/ejabberd-mod-mam.git"
|
|
@ -1,5 +0,0 @@
|
|||
author: "Lavrin"
|
||||
category: "admin"
|
||||
summary: "Easy tracing of connections made to ejabberd"
|
||||
home: "https://github.com/lavrin/ejabberd-trace/tree/master/"
|
||||
url: "git@github.com:lavrin/ejabberd-trace.git"
|
|
@ -1,5 +0,0 @@
|
|||
author: "Rael Max"
|
||||
category: "http"
|
||||
summary: "POST offline messages to a web"
|
||||
home: "https://github.com/raelmax/mod_http_offline/tree/master/"
|
||||
url: "git@github.com:raelmax/mod_http_offline.git"
|
|
@ -1,5 +0,0 @@
|
|||
author: "Jonas Ådahl <jadahl at gmail.com>"
|
||||
category: "http"
|
||||
summary: "RESTful API for ejabberd"
|
||||
home: "https://github.com/jadahl/mod_restful/tree/master/"
|
||||
url: "git@github.com:jadahl/mod_restful.git"
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
MODULES=`pwd`
|
||||
|
||||
# Put here the path to ejabberd source
|
||||
EJADIR=$MODULES/../git/ejabberd/
|
||||
|
||||
# TODO: Support extraction of multiple modules
|
||||
#MODULE=mod_webpresence
|
||||
MODULE=mod_register_web
|
||||
|
||||
RUNDIR=$MODULES/$MODULE/trunk/
|
||||
PREPARESCRIPT=$EJADIR/contrib/extract_translations/prepare-translation.sh
|
||||
|
||||
# 1. Create the directory $MODULE/msgs/
|
||||
|
||||
# 2. Create the $MODULE.pot
|
||||
#$PREPARESCRIPT -rundir $RUNDIR -ejadir $EJADIR -project $MODULE -src2pot
|
||||
|
||||
# 3. Create a language
|
||||
# cp $MODULE.pot $LANG.$MODULE.po
|
||||
# echo "" > $LANG.$MODULE.msg
|
||||
|
||||
# 3.b Convert msg to po. But it doesn't work! :(
|
||||
#$PREPARESCRIPT -rundir $RUNDIR -ejadir $EJADIR -project $MODULE -srcmsg2po we
|
||||
|
||||
# 4. Update strings
|
||||
$PREPARESCRIPT -rundir $RUNDIR -ejadir $EJADIR -project $MODULE -updateall
|
|
@ -0,0 +1 @@
|
|||
{'src/ejabberd_ircd', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
@ -1,15 +1,4 @@
|
|||
|
||||
|
||||
***************
|
||||
PLEASE NOTE
|
||||
***************
|
||||
|
||||
This module does NOT work
|
||||
with ejabberd 13 or newer.
|
||||
|
||||
***************
|
||||
|
||||
|
||||
ircd - IRC-to-XMPP interface
|
||||
|
||||
Author:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue