From ddae2cd8ddeeab3be5d3dc8f6624998bc8847323 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 7 Oct 2014 13:04:48 +0200 Subject: [PATCH] Remove mod_irc, it's included with ejabberd now --- mod_irc/Emakefile | 4 - mod_irc/LICENSE.txt | 343 ------- mod_irc/Makefile | 20 - mod_irc/README.txt | 121 --- mod_irc/build.bat | 1 - mod_irc/build.sh | 2 - mod_irc/ebin/.keepme | 0 mod_irc/src/iconv.erl | 94 -- mod_irc/src/iconv_erl.c | 155 ---- mod_irc/src/mod_irc.erl | 1016 --------------------- mod_irc/src/mod_irc_connection.erl | 1348 ---------------------------- 11 files changed, 3104 deletions(-) delete mode 100644 mod_irc/Emakefile delete mode 100644 mod_irc/LICENSE.txt delete mode 100644 mod_irc/Makefile delete mode 100644 mod_irc/README.txt delete mode 100644 mod_irc/build.bat delete mode 100755 mod_irc/build.sh delete mode 100644 mod_irc/ebin/.keepme delete mode 100644 mod_irc/src/iconv.erl delete mode 100644 mod_irc/src/iconv_erl.c delete mode 100644 mod_irc/src/mod_irc.erl delete mode 100644 mod_irc/src/mod_irc_connection.erl diff --git a/mod_irc/Emakefile b/mod_irc/Emakefile deleted file mode 100644 index 4923d52..0000000 --- a/mod_irc/Emakefile +++ /dev/null @@ -1,4 +0,0 @@ -{'../ejabberd-dev/src/gen_mod', [{outdir, "../ejabberd-dev/ebin"},{i,"../ejabberd-dev/include"}]}. -{'src/iconv', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}. -{'src/mod_irc', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}. -{'src/mod_irc_connection', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}. diff --git a/mod_irc/LICENSE.txt b/mod_irc/LICENSE.txt deleted file mode 100644 index 8b325f5..0000000 --- a/mod_irc/LICENSE.txt +++ /dev/null @@ -1,343 +0,0 @@ -As a special exception, the authors give permission to link this program -with the OpenSSL library and distribute the resulting binary. - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/mod_irc/Makefile b/mod_irc/Makefile deleted file mode 100644 index 3681098..0000000 --- a/mod_irc/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# XXX: these paths shouldn't be hardcoded -CC = gcc -CFLAGS = -O2 -Wall -CPPFLAGS = -I/usr/pkg/include -I/usr/local/lib/erlang/lib/erl_interface-3.5.5/include -I/usr/local/lib/erlang/usr/include -I/usr/local/ssl/lib -I/usr/pkg/include -LDFLAGS = -L/usr/pkg/lib -L/usr/local/lib/erlang/lib/erl_interface-3.5.5/lib -lerl_interface -lei -liconv - -# Assume Linux-style dynamic library flags -DYNAMIC_LIB_CFLAGS = -fpic -shared -ifeq ($(shell uname),Darwin) - DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress -endif -ifeq ($(shell uname),SunOs) - DYNAMIC_LIB_CFLAGS = -KPIC -G -z text -endif - -ebin/iconv_erl.so: src/iconv_erl.c - $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -o $@ $(DYNAMIC_LIB_CFLAGS) - -clean: - rm -f src/iconv_erl.so diff --git a/mod_irc/README.txt b/mod_irc/README.txt deleted file mode 100644 index e777ef7..0000000 --- a/mod_irc/README.txt +++ /dev/null @@ -1,121 +0,0 @@ - - mod_irc - IRC transport - - Author: Alexey Shchepin - Requires: GNU Iconv 1.8 or higher. Not needed on systems with GNU Libc. - http://www.ejabberd.im/mod_irc - - - DESCRIPTION - ----------- - -This module is an IRC transport that can be used to join channels on -IRC servers. - -This module was originally included in ejabberd releases. -It is not included since ejabberd 3.0.0. - - - INSTALL - ------- - -1. Compile the module - * On Windows: build.bat - * On other systems: ./build.sh; make - -2. Copy the beam files from ebin directory to your ejabberd ebin directory. - -3. Copy iconv_erl.so from ebin directory to your ejabberd binary -system libraries directory. -It may be something like /lib/ejabberd/priv/lib/ - -4. Edit ejabberd.cfg and add the module definition: -{modules, [ - {mod_irc, []}, - ... -]}. - -5. Restart ejabberd. -If problems appear, remember to always look first the ejabberd log files -ejabberd.log and sasl.log since they may provide some valuable information. - - - CONFIGURABLE PARAMETERS - ----------------------- - -host: This option defines the Jabber ID of the service. If the host - option is not specified, the Jabber ID will be the hostname of the - virtual host with the prefix ‘irc.’. The keyword "@HOST@" is - replaced at start time with the real virtual host name. - -access: This option can be used to specify who may use the IRC - transport (default value: all). - -default_encoding: Set the default IRC encoding (default value: - "koi8-r"). - - - EXAMPLE CONFIGURATION - --------------------- - - Example 1 - --------- - -In the first example, the IRC transport is available on (all) your -virtual host(s) with the prefix ‘irc.’. Furthermore, anyone is able to -use the transport. The default encoding is set to "iso8859-15". - -{modules, [ - {mod_irc, [{access, all}, - {default_encoding, "iso8859-15"}]}, - ... -]}. - - Example 2 - --------- - -In next example the IRC transport is available with JIDs with prefix -irc-t.net. Moreover, the transport is only accessible to two users of -example.org, and any user of example.com: - -{acl, paying_customers, {user, "customer1", "example.org"}}. -{acl, paying_customers, {user, "customer2", "example.org"}}. -{acl, paying_customers, {server, "example.com"}}. - -{access, irc_users, [{allow, paying_customers}, {deny, all}]}. - -{modules, [ - {mod_irc, [{access, irc_users}, - {host, "irc.example.net"}]}, - ... -]}. - - - USAGE - ----- - -How to use the IRC transport: - -* A Jabber client with ‘groupchat 1.0’ support or Multi-User Chat - support (XEP-0045) is necessary to join IRC channels. - -* An IRC channel can be joined in nearly the same way as joining a - Jabber Multi-User Chat room. The difference is that the room name - will be ‘channel%irc.example.org’ in case irc.example.org is the IRC - server hosting ‘channel’. And of course the host should point to the - IRC transport instead of the Multi-User Chat service. - -* You can register your nickame by sending ‘IDENTIFY password’ to - nickserver!irc.example.org@irc.jabberserver.org. - -* Entering your password is possible by sending ‘LOGIN nick password’ - to nickserver!irc.example.org@irc.jabberserver.org. - -* The IRC transport provides Ad-Hoc Commands (XEP-0050) to join a - channel, and to set custom IRC username and encoding. - -* When using a popular Jabber server, it can occur that no connection - can be achieved with some IRC servers because they limit the number - of conections from one IP. - - diff --git a/mod_irc/build.bat b/mod_irc/build.bat deleted file mode 100644 index b387ad5..0000000 --- a/mod_irc/build.bat +++ /dev/null @@ -1 +0,0 @@ -erl -pa ../ejabberd-dev/ebin -pa ebin -make diff --git a/mod_irc/build.sh b/mod_irc/build.sh deleted file mode 100755 index f0a0cfa..0000000 --- a/mod_irc/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -erl -pa ../ejabberd-dev/ebin -pz ebin -make diff --git a/mod_irc/ebin/.keepme b/mod_irc/ebin/.keepme deleted file mode 100644 index e69de29..0000000 diff --git a/mod_irc/src/iconv.erl b/mod_irc/src/iconv.erl deleted file mode 100644 index 8cccf2d..0000000 --- a/mod_irc/src/iconv.erl +++ /dev/null @@ -1,94 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : iconv.erl -%%% Author : Alexey Shchepin -%%% Purpose : Interface to libiconv -%%% Created : 16 Feb 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2009 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(iconv). --author('alexey@process-one.net'). - --behaviour(gen_server). - --export([start/0, start_link/0, convert/3]). - -%% Internal exports, call-back functions. --export([init/1, - handle_call/3, - handle_cast/2, - handle_info/2, - code_change/3, - terminate/2]). - - - -start() -> - gen_server:start({local, ?MODULE}, ?MODULE, [], []). - -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). - -init([]) -> - case erl_ddll:load_driver(ejabberd:get_so_path(), iconv_erl) of - ok -> ok; - {error, already_loaded} -> ok - end, - Port = open_port({spawn, iconv_erl}, []), - ets:new(iconv_table, [set, public, named_table]), - ets:insert(iconv_table, {port, Port}), - {ok, Port}. - - -%%% -------------------------------------------------------- -%%% The call-back functions. -%%% -------------------------------------------------------- - -handle_call(_, _, State) -> - {noreply, State}. - -handle_cast(_, State) -> - {noreply, State}. - -handle_info({'EXIT', Port, Reason}, Port) -> - {stop, {port_died, Reason}, Port}; -handle_info({'EXIT', _Pid, _Reason}, Port) -> - {noreply, Port}; -handle_info(_, State) -> - {noreply, State}. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -terminate(_Reason, Port) -> - Port ! {self, close}, - ok. - - - -convert(From, To, String) -> - [{port, Port} | _] = ets:lookup(iconv_table, port), - Bin = term_to_binary({From, To, String}), - BRes = port_control(Port, 1, Bin), - binary_to_list(BRes). - - - diff --git a/mod_irc/src/iconv_erl.c b/mod_irc/src/iconv_erl.c deleted file mode 100644 index f301bd5..0000000 --- a/mod_irc/src/iconv_erl.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * ejabberd, Copyright (C) 2002-2009 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 - * - */ - -#include -#include -#include -#include -#include - -typedef struct { - ErlDrvPort port; - iconv_t cd; -} iconv_data; - - -static ErlDrvData iconv_erl_start(ErlDrvPort port, char *buff) -{ - iconv_data* d = (iconv_data*)driver_alloc(sizeof(iconv_data)); - d->port = port; - d->cd = NULL; - - set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); - - return (ErlDrvData)d; -} - -static void iconv_erl_stop(ErlDrvData handle) -{ - driver_free((char*)handle); -} - -static int iconv_erl_control(ErlDrvData drv_data, - unsigned int command, - char *buf, int len, - char **rbuf, int rlen) -{ - int i; - int size; - int index = 0; - int avail; - size_t inleft, outleft; - ErlDrvBinary *b; - char *from, *to, *string, *stmp, *rstring, *rtmp; - iconv_t cd; - int invalid_utf8_as_latin1 = 0; - - ei_decode_version(buf, &index, &i); - ei_decode_tuple_header(buf, &index, &i); - ei_get_type(buf, &index, &i, &size); - from = malloc(size + 1); - ei_decode_string(buf, &index, from); - - ei_get_type(buf, &index, &i, &size); - to = malloc(size + 1); - ei_decode_string(buf, &index, to); - - ei_get_type(buf, &index, &i, &size); - stmp = string = malloc(size + 1); - ei_decode_string(buf, &index, string); - - /* Special mode: parse as UTF-8 if possible; otherwise assume it's - Latin-1. Makes no difference when encoding. */ - if (strcmp(from, "utf-8+latin-1") == 0) { - from[5] = '\0'; - invalid_utf8_as_latin1 = 1; - } - if (strcmp(to, "utf-8+latin-1") == 0) { - to[5] = '\0'; - } - cd = iconv_open(to, from); - - if (cd == (iconv_t) -1) { - cd = iconv_open("ascii", "ascii"); - if (cd == (iconv_t) -1) { - *rbuf = (char*)(b = driver_alloc_binary(size)); - memcpy(b->orig_bytes, string, size); - - free(from); - free(to); - free(string); - - return size; - } - } - - outleft = avail = 4*size; - inleft = size; - rtmp = rstring = malloc(avail); - while (inleft > 0) { - if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) { - if (invalid_utf8_as_latin1 && (*stmp & 0x80) && outleft >= 2) { - /* Encode one byte of (assumed) Latin-1 into two bytes of UTF-8 */ - *rtmp++ = 0xc0 | ((*stmp & 0xc0) >> 6); - *rtmp++ = 0x80 | (*stmp & 0x3f); - outleft -= 2; - } - stmp++; - inleft--; - } - } - - size = rtmp - rstring; - - *rbuf = (char*)(b = driver_alloc_binary(size)); - memcpy(b->orig_bytes, rstring, size); - - free(from); - free(to); - free(string); - free(rstring); - iconv_close(cd); - - return size; -} - - - -ErlDrvEntry iconv_driver_entry = { - NULL, /* F_PTR init, N/A */ - iconv_erl_start, /* L_PTR start, called when port is opened */ - iconv_erl_stop, /* F_PTR stop, called when port is closed */ - NULL, /* F_PTR output, called when erlang has sent */ - NULL, /* F_PTR ready_input, called when input descriptor ready */ - NULL, /* F_PTR ready_output, called when output descriptor ready */ - "iconv_erl", /* char *driver_name, the argument to open_port */ - NULL, /* F_PTR finish, called when unloaded */ - NULL, /* handle */ - iconv_erl_control, /* F_PTR control, port_command callback */ - NULL, /* F_PTR timeout, reserved */ - NULL /* F_PTR outputv, reserved */ -}; - -DRIVER_INIT(iconv_erl) /* must match name in driver_entry */ -{ - return &iconv_driver_entry; -} - - diff --git a/mod_irc/src/mod_irc.erl b/mod_irc/src/mod_irc.erl deleted file mode 100644 index 4b8b246..0000000 --- a/mod_irc/src/mod_irc.erl +++ /dev/null @@ -1,1016 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_irc.erl -%%% Author : Alexey Shchepin -%%% Purpose : IRC transport -%%% Created : 15 Feb 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2009 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(mod_irc). --author('alexey@process-one.net'). - --behaviour(gen_server). --behaviour(gen_mod). - -%% API --export([start_link/2, - start/2, - stop/1, - closed_connection/3, - get_connection_params/3]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --include("ejabberd.hrl"). --include("jlib.hrl"). --include("adhoc.hrl"). - --define(DEFAULT_IRC_ENCODING, "iso8859-1"). --define(DEFAULT_IRC_PORT, 6667). --define(POSSIBLE_ENCODINGS, ["koi8-r", "iso8859-1", "iso8859-2", "utf-8", "utf-8+latin-1"]). - --record(irc_connection, {jid_server_host, pid}). --record(irc_custom, {us_host, data}). - --record(state, {host, server_host, access}). - --define(PROCNAME, ejabberd_mod_irc). - -%%==================================================================== -%% API -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- -start_link(Host, Opts) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). - -start(Host, Opts) -> - start_supervisor(Host), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = - {Proc, - {?MODULE, start_link, [Host, Opts]}, - temporary, - 1000, - worker, - [?MODULE]}, - supervisor:start_child(ejabberd_sup, ChildSpec). - -stop(Host) -> - stop_supervisor(Host), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:call(Proc, stop), - supervisor:delete_child(ejabberd_sup, Proc). - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- -init([Host, Opts]) -> - iconv:start(), - mnesia:create_table(irc_custom, - [{disc_copies, [node()]}, - {attributes, record_info(fields, irc_custom)}]), - MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"), - update_table(MyHost), - Access = gen_mod:get_opt(access, Opts, all), - catch ets:new(irc_connection, [named_table, - public, - {keypos, #irc_connection.jid_server_host}]), - ejabberd_router:register_route(MyHost), - {ok, #state{host = MyHost, - server_host = Host, - access = Access}}. - -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- -handle_call(stop, _From, State) -> - {stop, normal, ok, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- -handle_info({route, From, To, Packet}, - #state{host = Host, - server_host = ServerHost, - access = Access} = State) -> - case catch do_route(Host, ServerHost, Access, From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - _ -> - ok - end, - {noreply, State}; -handle_info(_Info, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- -terminate(_Reason, State) -> - ejabberd_router:unregister_route(State#state.host), - ok. - -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- -start_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), - ChildSpec = - {Proc, - {ejabberd_tmp_sup, start_link, - [Proc, mod_irc_connection]}, - permanent, - infinity, - supervisor, - [ejabberd_tmp_sup]}, - supervisor:start_child(ejabberd_sup, ChildSpec). - -stop_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), - supervisor:terminate_child(ejabberd_sup, Proc), - supervisor:delete_child(ejabberd_sup, Proc). - -do_route(Host, ServerHost, Access, From, To, Packet) -> - case acl:match_rule(ServerHost, Access, From) of - allow -> - do_route1(Host, ServerHost, From, To, Packet); - _ -> - {xmlelement, _Name, Attrs, _Els} = Packet, - Lang = xml:get_attr_s("xml:lang", Attrs), - ErrText = "Access denied by service policy", - Err = jlib:make_error_reply(Packet, - ?ERRT_FORBIDDEN(Lang, ErrText)), - ejabberd_router:route(To, From, Err) - end. - -do_route1(Host, ServerHost, From, To, Packet) -> - #jid{user = ChanServ, resource = Resource} = To, - {xmlelement, _Name, _Attrs, _Els} = Packet, - case ChanServ of - "" -> - case Resource of - "" -> - case jlib:iq_query_info(Packet) of - #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, - sub_el = SubEl, lang = Lang} = IQ -> - Node = xml:get_tag_attr_s("node", SubEl), - Info = ejabberd_hooks:run_fold( - disco_info, ServerHost, [], - [ServerHost, ?MODULE, "", ""]), - case iq_disco(Node, Lang) of - [] -> - Res = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - []}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - DiscoInfo -> - Res = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - DiscoInfo ++ Info}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)) - end; - #iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS, - sub_el = SubEl, lang = Lang} = IQ -> - Node = xml:get_tag_attr_s("node", SubEl), - case Node of - [] -> - ResIQ = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - []}]}, - Res = jlib:iq_to_xml(ResIQ); - "join" -> - ResIQ = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - []}]}, - Res = jlib:iq_to_xml(ResIQ); - "register" -> - ResIQ = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - []}]}, - Res = jlib:iq_to_xml(ResIQ); - ?NS_COMMANDS -> - ResIQ = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}, - {"node", Node}], - command_items(Host, Lang)}]}, - Res = jlib:iq_to_xml(ResIQ); - _ -> - Res = jlib:make_error_reply( - Packet, ?ERR_ITEM_NOT_FOUND) - end, - ejabberd_router:route(To, - From, - Res); - #iq{xmlns = ?NS_REGISTER} = IQ -> - process_register(Host, From, To, IQ); - #iq{type = get, xmlns = ?NS_VCARD = XMLNS, - lang = Lang} = IQ -> - Res = IQ#iq{type = result, - sub_el = - [{xmlelement, "vCard", - [{"xmlns", XMLNS}], - iq_get_vcard(Lang)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{type = set, xmlns = ?NS_COMMANDS, - lang = _Lang, sub_el = SubEl} = IQ -> - Request = adhoc:parse_request(IQ), - case lists:keysearch(Request#adhoc_request.node, 1, commands()) of - {value, {_, _, Function}} -> - case catch Function(From, To, Request) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nfor ad-hoc handler of ~p", - [Reason, {From, To, IQ}]), - Res = IQ#iq{type = error, sub_el = [SubEl, - ?ERR_INTERNAL_SERVER_ERROR]}; - ignore -> - Res = ignore; - {error, Error} -> - Res = IQ#iq{type = error, sub_el = [SubEl, Error]}; - Command -> - Res = IQ#iq{type = result, sub_el = [Command]} - end, - if Res /= ignore -> - ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); - true -> - ok - end; - _ -> - Err = jlib:make_error_reply( - Packet, ?ERR_ITEM_NOT_FOUND), - ejabberd_router:route(To, From, Err) - end; - #iq{} = _IQ -> - Err = jlib:make_error_reply( - Packet, ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - _ -> - ok - end; - _ -> - Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST), - ejabberd_router:route(To, From, Err) - end; - _ -> - case string:tokens(ChanServ, "%") of - [[_ | _] = Channel, [_ | _] = Server] -> - case ets:lookup(irc_connection, {From, Server, Host}) of - [] -> - ?DEBUG("open new connection~n", []), - {Username, Encoding, Port, Password} = get_connection_params( - Host, From, Server), - ConnectionUsername = - case Packet of - %% If the user tries to join a - %% chatroom, the packet for sure - %% contains the desired username. - {xmlelement, "presence", _, _} -> - Resource; - %% Otherwise, there is no firm - %% conclusion from the packet. - %% Better to use the configured - %% username (which defaults to the - %% username part of the JID). - _ -> - Username - end, - {ok, Pid} = mod_irc_connection:start( - From, Host, ServerHost, Server, - ConnectionUsername, Encoding, Port, Password), - ets:insert( - irc_connection, - #irc_connection{jid_server_host = {From, Server, Host}, - pid = Pid}), - mod_irc_connection:route_chan( - Pid, Channel, Resource, Packet), - ok; - [R] -> - Pid = R#irc_connection.pid, - ?DEBUG("send to process ~p~n", - [Pid]), - mod_irc_connection:route_chan( - Pid, Channel, Resource, Packet), - ok - end; - _ -> - case string:tokens(ChanServ, "!") of - [[_ | _] = Nick, [_ | _] = Server] -> - case ets:lookup(irc_connection, {From, Server, Host}) of - [] -> - Err = jlib:make_error_reply( - Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err); - [R] -> - Pid = R#irc_connection.pid, - ?DEBUG("send to process ~p~n", - [Pid]), - mod_irc_connection:route_nick( - Pid, Nick, Packet), - ok - end; - _ -> - Err = jlib:make_error_reply( - Packet, ?ERR_BAD_REQUEST), - ejabberd_router:route(To, From, Err) - end - end - end. - - -closed_connection(Host, From, Server) -> - ets:delete(irc_connection, {From, Server, Host}). - - -iq_disco([], Lang) -> - [{xmlelement, "identity", - [{"category", "conference"}, - {"type", "irc"}, - {"name", translate:translate(Lang, "IRC Transport")}], []}, - {xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []}, - {xmlelement, "feature", [{"var", ?NS_MUC}], []}, - {xmlelement, "feature", [{"var", ?NS_REGISTER}], []}, - {xmlelement, "feature", [{"var", ?NS_VCARD}], []}, - {xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}]; -iq_disco(Node, Lang) -> - case lists:keysearch(Node, 1, commands()) of - {value, {_, Name, _}} -> - [{xmlelement, "identity", - [{"category", "automation"}, - {"type", "command-node"}, - {"name", translate:translate(Lang, Name)}], []}, - {xmlelement, "feature", - [{"var", ?NS_COMMANDS}], []}, - {xmlelement, "feature", - [{"var", ?NS_XDATA}], []}]; - _ -> - [] - end. - -iq_get_vcard(Lang) -> - [{xmlelement, "FN", [], - [{xmlcdata, "ejabberd/mod_irc"}]}, - {xmlelement, "URL", [], - [{xmlcdata, ?EJABBERD_URI}]}, - {xmlelement, "DESC", [], - [{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++ - "\nCopyright (c) 2003-2009 Alexey Shchepin"}]}]. - -command_items(Host, Lang) -> - lists:map(fun({Node, Name, _Function}) - -> {xmlelement, "item", - [{"jid", Host}, - {"node", Node}, - {"name", translate:translate(Lang, Name)}], []} - end, commands()). - -commands() -> - [{"join", "Join channel", fun adhoc_join/3}, - {"register", "Configure username, encoding, port and password", fun adhoc_register/3}]. - -process_register(Host, From, To, #iq{} = IQ) -> - case catch process_irc_register(Host, From, To, IQ) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - ResIQ -> - if - ResIQ /= ignore -> - ejabberd_router:route(To, From, - jlib:iq_to_xml(ResIQ)); - true -> - ok - end - end. - -find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) -> - find_xdata_el1(SubEls). - -find_xdata_el1([]) -> - false; - -find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) -> - case xml:get_attr_s("xmlns", Attrs) of - ?NS_XDATA -> - {xmlelement, Name, Attrs, SubEls}; - _ -> - find_xdata_el1(Els) - end; - -find_xdata_el1([_ | Els]) -> - find_xdata_el1(Els). - -process_irc_register(Host, From, _To, - #iq{type = Type, xmlns = XMLNS, - lang = Lang, sub_el = SubEl} = IQ) -> - case Type of - set -> - XDataEl = find_xdata_el(SubEl), - case XDataEl of - false -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]}; - {xmlelement, _Name, Attrs, _SubEls} -> - case xml:get_attr_s("type", Attrs) of - "cancel" -> - IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], []}]}; - "submit" -> - XData = jlib:parse_xdata_submit(XDataEl), - case XData of - invalid -> - IQ#iq{type = error, - sub_el = [SubEl, ?ERR_BAD_REQUEST]}; - _ -> - Node = string:tokens( - xml:get_tag_attr_s("node", SubEl), - "/"), - case set_form( - Host, From, Node, Lang, XData) of - {result, Res} -> - IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - Res - }]}; - {error, Error} -> - IQ#iq{type = error, - sub_el = [SubEl, Error]} - end - end; - _ -> - IQ#iq{type = error, - sub_el = [SubEl, ?ERR_BAD_REQUEST]} - end - end; - get -> - Node = - string:tokens(xml:get_tag_attr_s("node", SubEl), "/"), - case get_form(Host, From, Node, Lang) of - {result, Res} -> - IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - Res - }]}; - {error, Error} -> - IQ#iq{type = error, - sub_el = [SubEl, Error]} - end - end. - - - -get_form(Host, From, [], Lang) -> - #jid{user = User, server = Server, - luser = LUser, lserver = LServer} = From, - US = {LUser, LServer}, - Customs = - case catch mnesia:dirty_read({irc_custom, {US, Host}}) of - {'EXIT', _Reason} -> - {error, ?ERR_INTERNAL_SERVER_ERROR}; - [] -> - {User, []}; - [#irc_custom{data = Data}] -> - {xml:get_attr_s(username, Data), - xml:get_attr_s(connections_params, Data)} - end, - case Customs of - {error, _Error} -> - Customs; - {Username, ConnectionsParams} -> - {result, - [{xmlelement, "instructions", [], - [{xmlcdata, - translate:translate( - Lang, - "You need an x:data capable client " - "to configure mod_irc settings")}]}, - {xmlelement, "x", [{"xmlns", ?NS_XDATA}], - [{xmlelement, "title", [], - [{xmlcdata, - translate:translate( - Lang, - "Registration in mod_irc for ") ++ User ++ "@" ++ Server}]}, - {xmlelement, "instructions", [], - [{xmlcdata, - translate:translate( - Lang, - "Enter username, encodings, ports and passwords you wish to use for " - "connecting to IRC servers")}]}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", - translate:translate( - Lang, "IRC Username")}, - {"var", "username"}], - [{xmlelement, "value", [], [{xmlcdata, Username}]}]}, - {xmlelement, "field", [{"type", "fixed"}], - [{xmlelement, "value", [], - [{xmlcdata, - lists:flatten( - io_lib:format( - translate:translate( - Lang, - "If you want to specify different ports, " - "passwords, encodings for IRC servers, fill " - "this list with values in format " - "'{\"irc server\", \"encoding\", port, \"password\"}'. " - "By default this service use \"~s\" encoding, port ~p, " - "empty password."), - [?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT]))}]}]}, - {xmlelement, "field", [{"type", "fixed"}], - [{xmlelement, "value", [], - [{xmlcdata, - translate:translate( - Lang, - "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, " - "{\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - )}]}]}, - {xmlelement, "field", [{"type", "text-multi"}, - {"label", - translate:translate(Lang, "Connections parameters")}, - {"var", "connections_params"}], - lists:map( - fun(S) -> - {xmlelement, "value", [], [{xmlcdata, S}]} - end, - string:tokens( - lists:flatten( - io_lib:format("~p.", [ConnectionsParams])), - "\n")) - } - ]}]} - end; - -get_form(_Host, _, _, _Lang) -> - {error, ?ERR_SERVICE_UNAVAILABLE}. - - - - -set_form(Host, From, [], _Lang, XData) -> - {LUser, LServer, _} = jlib:jid_tolower(From), - US = {LUser, LServer}, - case {lists:keysearch("username", 1, XData), - lists:keysearch("connections_params", 1, XData)} of - {{value, {_, [Username]}}, {value, {_, Strings}}} -> - EncString = lists:foldl(fun(S, Res) -> - Res ++ S ++ "\n" - end, "", Strings), - case erl_scan:string(EncString) of - {ok, Tokens, _} -> - case erl_parse:parse_term(Tokens) of - {ok, ConnectionsParams} -> - case mnesia:transaction( - fun() -> - mnesia:write( - #irc_custom{us_host = - {US, Host}, - data = - [{username, - Username}, - {connections_params, - ConnectionsParams}]}) - end) of - {atomic, _} -> - {result, []}; - _ -> - {error, ?ERR_NOT_ACCEPTABLE} - end; - _ -> - {error, ?ERR_NOT_ACCEPTABLE} - end; - _ -> - {error, ?ERR_NOT_ACCEPTABLE} - end; - _ -> - {error, ?ERR_NOT_ACCEPTABLE} - end; - - -set_form(_Host, _, _, _Lang, _XData) -> - {error, ?ERR_SERVICE_UNAVAILABLE}. - - -get_connection_params(Host, From, IRCServer) -> - #jid{user = User, server = _Server, - luser = LUser, lserver = LServer} = From, - US = {LUser, LServer}, - case catch mnesia:dirty_read({irc_custom, {US, Host}}) of - {'EXIT', _Reason} -> - {User, ?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT, ""}; - [] -> - {User, ?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT, ""}; - [#irc_custom{data = Data}] -> - Username = xml:get_attr_s(username, Data), - {NewUsername, NewEncoding, NewPort, NewPassword} = - case lists:keysearch(IRCServer, 1, xml:get_attr_s(connections_params, Data)) of - {value, {_, Encoding, Port, Password}} -> - {Username, Encoding, Port, Password}; - {value, {_, Encoding, Port}} -> - {Username, Encoding, Port, ""}; - {value, {_, Encoding}} -> - {Username, Encoding, ?DEFAULT_IRC_PORT, ""}; - _ -> - {Username, ?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT, ""} - end, - {NewUsername, - NewEncoding, - if - NewPort >= 0 andalso NewPort =< 65535 -> - NewPort; - true -> - ?DEFAULT_IRC_PORT - end, - NewPassword} - end. - -adhoc_join(_From, _To, #adhoc_request{action = "cancel"} = Request) -> - adhoc:produce_response(Request, - #adhoc_response{status = canceled}); -adhoc_join(From, To, #adhoc_request{lang = Lang, - node = _Node, - action = _Action, - xdata = XData} = Request) -> - %% Access control has already been taken care of in do_route. - if XData == false -> - Form = - {xmlelement, "x", - [{"xmlns", ?NS_XDATA}, - {"type", "form"}], - [{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "Join IRC channel")}]}, - {xmlelement, "field", - [{"var", "channel"}, - {"type", "text-single"}, - {"label", translate:translate(Lang, "IRC channel (don't put the first #)")}], - [{xmlelement, "required", [], []}]}, - {xmlelement, "field", - [{"var", "server"}, - {"type", "text-single"}, - {"label", translate:translate(Lang, "IRC server")}], - [{xmlelement, "required", [], []}]}]}, - adhoc:produce_response(Request, - #adhoc_response{status = executing, - elements = [Form]}); - true -> - case jlib:parse_xdata_submit(XData) of - invalid -> - {error, ?ERR_BAD_REQUEST}; - Fields -> - Channel = case lists:keysearch("channel", 1, Fields) of - {value, {"channel", C}} -> - C; - _ -> - false - end, - Server = case lists:keysearch("server", 1, Fields) of - {value, {"server", S}} -> - S; - _ -> - false - end, - if Channel /= false, - Server /= false -> - RoomJID = Channel ++ "%" ++ Server ++ "@" ++ To#jid.server, - Invite = {xmlelement, "message", [], - [{xmlelement, "x", - [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "invite", - [{"from", jlib:jid_to_string(From)}], - [{xmlelement, "reason", [], - [{xmlcdata, - translate:translate(Lang, - "Join the IRC channel here.")}]}]}]}, - {xmlelement, "x", - [{"xmlns", ?NS_XCONFERENCE}], - [{xmlcdata, translate:translate(Lang, - "Join the IRC channel here.")}]}, - {xmlelement, "body", [], - [{xmlcdata, io_lib:format( - translate:translate(Lang, - "Join the IRC channel in this Jabber ID: ~s"), - [RoomJID])}]}]}, - ejabberd_router:route(jlib:string_to_jid(RoomJID), From, Invite), - adhoc:produce_response(Request, #adhoc_response{status = completed}); - true -> - {error, ?ERR_BAD_REQUEST} - end - end - end. - -adhoc_register(_From, _To, #adhoc_request{action = "cancel"} = Request) -> - adhoc:produce_response(Request, - #adhoc_response{status = canceled}); -adhoc_register(From, To, #adhoc_request{lang = Lang, - node = _Node, - xdata = XData, - action = Action} = Request) -> - #jid{user = User, luser = LUser, lserver = LServer} = From, - #jid{lserver = Host} = To, - US = {LUser, LServer}, - %% Generate form for setting username and encodings. If the user - %% hasn't begun to fill out the form, generate an initial form - %% based on current values. - if XData == false -> - case catch mnesia:dirty_read({irc_custom, {US, Host}}) of - {'EXIT', _Reason} -> - Username = User, - ConnectionsParams = []; - [] -> - Username = User, - ConnectionsParams = []; - [#irc_custom{data = Data}] -> - Username = xml:get_attr_s(username, Data), - ConnectionsParams = xml:get_attr_s(connections_params, Data) - end, - Error = false; - true -> - case jlib:parse_xdata_submit(XData) of - invalid -> - Error = {error, ?ERR_BAD_REQUEST}, - Username = false, - ConnectionsParams = false; - Fields -> - Username = case lists:keysearch("username", 1, Fields) of - {value, {"username", U}} -> - U; - _ -> - User - end, - ConnectionsParams = parse_connections_params(Fields), - Error = false - end - end, - - if Error /= false -> - Error; - Action == "complete" -> - case mnesia:transaction( - fun () -> - mnesia:write( - #irc_custom{us_host = - {US, Host}, - data = - [{username, - Username}, - {connections_params, - ConnectionsParams}]}) - end) of - {atomic, _} -> - adhoc:produce_response(Request, #adhoc_response{status = completed}); - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end; - true -> - Form = generate_adhoc_register_form(Lang, Username, ConnectionsParams), - adhoc:produce_response(Request, - #adhoc_response{status = executing, - elements = [Form], - actions = ["next", "complete"]}) - end. - -generate_adhoc_register_form(Lang, Username, ConnectionsParams) -> - {xmlelement, "x", - [{"xmlns", ?NS_XDATA}, - {"type", "form"}], - [{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "IRC settings")}]}, - {xmlelement, "instructions", [], - [{xmlcdata, - translate:translate( - Lang, - "Enter username and encodings you wish to use for " - "connecting to IRC servers. Press 'Next' to get more fields " - "to fill in. Press 'Complete' to save settings.")}]}, - {xmlelement, "field", - [{"var", "username"}, - {"type", "text-single"}, - {"label", translate:translate(Lang, "IRC username")}], - [{xmlelement, "required", [], []}, - {xmlelement, "value", [], [{xmlcdata, Username}]}]}] ++ - generate_connection_params_fields(Lang, ConnectionsParams, 1, [])}. - -generate_connection_params_fields(Lang, [], Number, Acc) -> - Field = generate_connection_params_field(Lang, "", "", -1, "", Number), - lists:reverse(Field ++ Acc); - -generate_connection_params_fields(Lang, [ConnectionParams | ConnectionsParams], Number, Acc) -> - case ConnectionParams of - {Server, Encoding, Port, Password} -> - Field = generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number), - generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc); - {Server, Encoding, Port} -> - Field = generate_connection_params_field(Lang, Server, Encoding, Port, [], Number), - generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc); - {Server, Encoding} -> - Field = generate_connection_params_field(Lang, Server, Encoding, [], [], Number), - generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc); - _ -> - [] - end. - -generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number) -> - EncodingUsed = case Encoding of - [] -> - ?DEFAULT_IRC_ENCODING; - _ -> - Encoding - end, - PortUsedInt = if - Port >= 0 andalso Port =< 65535 -> - Port; - true -> - ?DEFAULT_IRC_PORT - end, - PortUsed = integer_to_list(PortUsedInt), - PasswordUsed = case Password of - [] -> - ""; - _ -> - Password - end, - NumberString = integer_to_list(Number), - %% Fields are in reverse order, as they will be reversed again later. - [{xmlelement, "field", - [{"var", "password" ++ NumberString}, - {"type", "text-single"}, - {"label", io_lib:format(translate:translate(Lang, "Password ~b"), [Number])}], - [{xmlelement, "value", [], [{xmlcdata, PasswordUsed}]}]}, - {xmlelement, "field", - [{"var", "port" ++ NumberString}, - {"type", "text-single"}, - {"label", io_lib:format(translate:translate(Lang, "Port ~b"), [Number])}], - [{xmlelement, "value", [], [{xmlcdata, PortUsed}]}]}, - {xmlelement, "field", - [{"var", "encoding" ++ NumberString}, - {"type", "list-single"}, - {"label", io_lib:format(translate:translate(Lang, "Encoding for server ~b"), [Number])}], - [{xmlelement, "value", [], [{xmlcdata, EncodingUsed}]} | - lists:map(fun(E) -> - {xmlelement, "option", [{"label", E}], - [{xmlelement, "value", [], [{xmlcdata, E}]}]} - end, ?POSSIBLE_ENCODINGS)]}, - {xmlelement, "field", - [{"var", "server" ++ NumberString}, - {"type", "text-single"}, - {"label", io_lib:format(translate:translate(Lang, "Server ~b"), [Number])}], - [{xmlelement, "value", [], [{xmlcdata, Server}]}]}]. - -parse_connections_params(Fields) -> - %% Find all fields staring with serverN, encodingN, portN and passwordN for any values - %% of N, and generate lists of {"N", Value}. - Servers = lists:sort( - [{lists:nthtail(6, Var), lists:flatten(Value)} || {Var, Value} <- Fields, - lists:prefix("server", Var)]), - Encodings = lists:sort( - [{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields, - lists:prefix("encoding", Var)]), - - Ports = lists:sort( - [{lists:nthtail(4, Var), lists:flatten(Value)} || {Var, Value} <- Fields, - lists:prefix("port", Var)]), - - Passwords = lists:sort( - [{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields, - lists:prefix("password", Var)]), - - %% Now sort the lists, and find the corresponding pairs. - parse_connections_params(Servers, Encodings, Ports, Passwords). - -retrieve_connections_params(ConnectionParams, ServerN) -> - case ConnectionParams of - [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail] -> - if - ServerN == ConnectionParamN -> - {ConnectionParam, ConnectionParamsTail}; - ServerN < ConnectionParamN -> - {[], [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail]}; - ServerN > ConnectionParamN -> - {[], ConnectionParamsTail} - end; - _ -> - {[], []} - end. - -parse_connections_params([], _, _, _) -> - []; -parse_connections_params(_, [], [], []) -> - []; - -parse_connections_params([{ServerN, Server} | Servers], Encodings, Ports, Passwords) -> - %% Try to match matches of servers, ports, passwords and encodings, no matter what fields - %% the client might have left out. - {NewEncoding, NewEncodings} = retrieve_connections_params(Encodings, ServerN), - {NewPort, NewPorts} = retrieve_connections_params(Ports, ServerN), - {NewPassword, NewPasswords} = retrieve_connections_params(Passwords, ServerN), - [{Server, NewEncoding, NewPort, NewPassword} | parse_connections_params(Servers, NewEncodings, NewPorts, NewPasswords)]. - -update_table(Host) -> - Fields = record_info(fields, irc_custom), - case mnesia:table_info(irc_custom, attributes) of - Fields -> - ok; - [userserver, data] -> - ?INFO_MSG("Converting irc_custom table from " - "{userserver, data} format", []), - {atomic, ok} = mnesia:create_table( - mod_irc_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, irc_custom}, - {attributes, record_info(fields, irc_custom)}]), - mnesia:transform_table(irc_custom, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_irc_tmp_table), - mnesia:foldl( - fun(#irc_custom{us_host = US} = R, _) -> - mnesia:dirty_write( - mod_irc_tmp_table, - R#irc_custom{us_host = {US, Host}}) - end, ok, irc_custom) - end, - mnesia:transaction(F1), - mnesia:clear_table(irc_custom), - F2 = fun() -> - mnesia:write_lock_table(irc_custom), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_irc_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_irc_tmp_table); - _ -> - ?INFO_MSG("Recreating irc_custom table", []), - mnesia:transform_table(irc_custom, ignore, Fields) - end. diff --git a/mod_irc/src/mod_irc_connection.erl b/mod_irc/src/mod_irc_connection.erl deleted file mode 100644 index 252c425..0000000 --- a/mod_irc/src/mod_irc_connection.erl +++ /dev/null @@ -1,1348 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_irc_connection.erl -%%% Author : Alexey Shchepin -%%% Purpose : -%%% Created : 15 Feb 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2009 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(mod_irc_connection). --author('alexey@process-one.net'). - --behaviour(gen_fsm). - -%% External exports --export([start_link/7, start/8, route_chan/4, route_nick/3]). - -%% gen_fsm callbacks --export([init/1, - open_socket/2, - wait_for_registration/2, - stream_established/2, - handle_event/3, - handle_sync_event/4, - handle_info/3, - terminate/3, - code_change/4]). - --include("ejabberd.hrl"). --include("jlib.hrl"). - --define(SETS, gb_sets). - --record(state, {socket, encoding, port, password, - queue, user, host, server, nick, - channels = dict:new(), - nickchannel, - inbuf = "", outbuf = ""}). - -%-define(DBGFSM, true). - --ifdef(DBGFSM). --define(FSMOPTS, [{debug, [trace]}]). --else. --define(FSMOPTS, []). --endif. - -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- -start(From, Host, ServerHost, Server, Username, Encoding, Port, Password) -> - Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_irc_sup), - supervisor:start_child( - Supervisor, [From, Host, Server, Username, Encoding, Port, Password]). - -start_link(From, Host, Server, Username, Encoding, Port, Password) -> - gen_fsm:start_link(?MODULE, [From, Host, Server, Username, Encoding, Port, Password], - ?FSMOPTS). - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_fsm -%%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, StateName, StateData} | -%% {ok, StateName, StateData, Timeout} | -%% ignore | -%% {stop, StopReason} -%%---------------------------------------------------------------------- -init([From, Host, Server, Username, Encoding, Port, Password]) -> - gen_fsm:send_event(self(), init), - {ok, open_socket, #state{queue = queue:new(), - encoding = Encoding, - port = Port, - password = Password, - user = From, - nick = Username, - host = Host, - server = Server}}. - -%%---------------------------------------------------------------------- -%% Func: StateName/2 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -open_socket(init, StateData) -> - Addr = StateData#state.server, - Port = StateData#state.port, - ?DEBUG("Connecting with IPv6 to ~s:~p", [Addr, Port]), - Connect6 = gen_tcp:connect(Addr, Port, [inet6, binary, {packet, 0}]), - Connect = case Connect6 of - {error, _} -> - ?DEBUG("Connection with IPv6 to ~s:~p failed. Now using IPv4.", [Addr, Port]), - gen_tcp:connect(Addr, Port, [inet, binary, {packet, 0}]); - _ -> - Connect6 - end, - case Connect of - {ok, Socket} -> - NewStateData = StateData#state{socket = Socket}, - if - StateData#state.password /= "" -> - send_text(NewStateData, - io_lib:format("PASS ~s\r\n", [StateData#state.password])); - true -> true - end, - send_text(NewStateData, - io_lib:format("NICK ~s\r\n", [StateData#state.nick])), - send_text(NewStateData, - io_lib:format( - "USER ~s ~s ~s :~s\r\n", - [StateData#state.nick, - StateData#state.nick, - StateData#state.host, - StateData#state.nick])), - send_text(NewStateData, - io_lib:format("CODEPAGE ~s\r\n", [StateData#state.encoding])), - {next_state, wait_for_registration, - NewStateData}; - {error, Reason} -> - ?DEBUG("connect return ~p~n", [Reason]), - Text = case Reason of - timeout -> "Server Connect Timeout"; - _ -> "Server Connect Failed" - end, - bounce_messages(Text), - {stop, normal, StateData} - end. - -wait_for_registration(closed, StateData) -> - {stop, normal, StateData}. - -stream_established({xmlstreamend, _Name}, StateData) -> - {stop, normal, StateData}; - -stream_established(timeout, StateData) -> - {stop, normal, StateData}; - -stream_established(closed, StateData) -> - {stop, normal, StateData}. - - - -%%---------------------------------------------------------------------- -%% Func: StateName/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {reply, Reply, NextStateName, NextStateData} | -%% {reply, Reply, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} | -%% {stop, Reason, Reply, NewStateData} -%%---------------------------------------------------------------------- -%state_name(Event, From, StateData) -> -% Reply = ok, -% {reply, Reply, state_name, StateData}. - -%%---------------------------------------------------------------------- -%% Func: handle_event/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -handle_event(_Event, StateName, StateData) -> - {next_state, StateName, StateData}. - -%%---------------------------------------------------------------------- -%% Func: handle_sync_event/4 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {reply, Reply, NextStateName, NextStateData} | -%% {reply, Reply, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} | -%% {stop, Reason, Reply, NewStateData} -%%---------------------------------------------------------------------- -handle_sync_event(_Event, _From, StateName, StateData) -> - Reply = ok, - {reply, Reply, StateName, StateData}. - -code_change(_OldVsn, StateName, StateData, _Extra) -> - {ok, StateName, StateData}. - --define(SEND(S), - if - StateName == stream_established -> - send_text(StateData, S), - StateData; - true -> - StateData#state{outbuf = StateData#state.outbuf ++ S} - end). - -%%---------------------------------------------------------------------- -%% Func: handle_info/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -handle_info({route_chan, Channel, Resource, - {xmlelement, "presence", Attrs, _Els}}, - StateName, StateData) -> - NewStateData = - case xml:get_attr_s("type", Attrs) of - "unavailable" -> - send_stanza_unavailable(Channel, StateData), - S1 = ?SEND(io_lib:format("PART #~s\r\n", [Channel])), - S1#state{channels = - dict:erase(Channel, S1#state.channels)}; - "subscribe" -> StateData; - "subscribed" -> StateData; - "unsubscribe" -> StateData; - "unsubscribed" -> StateData; - "error" -> stop; - _ -> - Nick = case Resource of - "" -> - StateData#state.nick; - _ -> - Resource - end, - S1 = if - Nick /= StateData#state.nick -> - S11 = ?SEND(io_lib:format("NICK ~s\r\n", [Nick])), - % The server reply will change the copy of the - % nick in the state (or indicate a clash). - S11#state{nickchannel = Channel}; - true -> - StateData - end, - case dict:is_key(Channel, S1#state.channels) of - true -> - S1; - _ -> - S2 = ?SEND(io_lib:format("JOIN #~s\r\n", [Channel])), - S2#state{channels = - dict:store(Channel, ?SETS:new(), - S1#state.channels)} - end - end, - if - NewStateData == stop -> - {stop, normal, StateData}; - true -> - case dict:fetch_keys(NewStateData#state.channels) of - [] -> {stop, normal, NewStateData}; - _ -> {next_state, StateName, NewStateData} - end - end; - -handle_info({route_chan, Channel, Resource, - {xmlelement, "message", Attrs, _Els} = El}, - StateName, StateData) -> - NewStateData = - case xml:get_attr_s("type", Attrs) of - "groupchat" -> - case xml:get_path_s(El, [{elem, "subject"}, cdata]) of - "" -> - ejabberd_router:route( - jlib:make_jid( - lists:concat( - [Channel, "%", StateData#state.server]), - StateData#state.host, StateData#state.nick), - StateData#state.user, El), - Body = xml:get_path_s(El, [{elem, "body"}, cdata]), - case Body of - "/quote " ++ Rest -> - ?SEND(Rest ++ "\r\n"); - "/msg " ++ Rest -> - ?SEND("PRIVMSG " ++ Rest ++ "\r\n"); - "/me " ++ Rest -> - Strings = string:tokens(Rest, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format( - "PRIVMSG #~s :\001ACTION ~s\001\r\n", - [Channel, S]) - end, Strings)), - ?SEND(Res); - "/ctcp " ++ Rest -> - Words = string:tokens(Rest, " "), - case Words of - [CtcpDest | _] -> - CtcpCmd = - toupper( - string:substr( - Rest, - string:str(Rest, " ") + 1)), - Res = io_lib:format( - "PRIVMSG ~s :\001~s\001\r\n", - [CtcpDest, CtcpCmd]), - ?SEND(Res); - _ -> - ok - end; - _ -> - Strings = string:tokens(Body, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format( - "PRIVMSG #~s :~s\r\n", - [Channel, S]) - end, Strings)), - ?SEND(Res) - end; - Subject -> - Strings = string:tokens(Subject, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format("TOPIC #~s :~s\r\n", - [Channel, S]) - end, Strings)), - ?SEND(Res) - end; - Type when Type == "chat"; Type == ""; Type == "normal" -> - Body = xml:get_path_s(El, [{elem, "body"}, cdata]), - case Body of - "/quote " ++ Rest -> - ?SEND(Rest ++ "\r\n"); - "/msg " ++ Rest -> - ?SEND("PRIVMSG " ++ Rest ++ "\r\n"); - "/me " ++ Rest -> - Strings = string:tokens(Rest, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format( - "PRIVMSG ~s :\001ACTION ~s\001\r\n", - [Resource, S]) - end, Strings)), - ?SEND(Res); - "/ctcp " ++ Rest -> - Words = string:tokens(Rest, " "), - case Words of - [CtcpDest | _ ] -> - CtcpCmd = - toupper( - string:substr( - Rest, string:str(Rest, " ") + 1)), - Res = io_lib:format( - "PRIVMSG ~s :~s\r\n", - [CtcpDest, "\001" ++ CtcpCmd ++ "\001"]), - ?SEND(Res); - _ -> - ok - end; - _ -> - Strings = string:tokens(Body, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format("PRIVMSG ~s :~s\r\n", - [Resource, S]) - end, Strings)), - ?SEND(Res) - end; - "error" -> - stop; - _ -> - StateData - end, - if - NewStateData == stop -> - {stop, normal, StateData}; - true -> - {next_state, StateName, NewStateData} - end; - - -handle_info({route_chan, Channel, Resource, - {xmlelement, "iq", _Attrs, _Els} = El}, - StateName, StateData) -> - From = StateData#state.user, - To = jlib:make_jid(lists:concat([Channel, "%", StateData#state.server]), - StateData#state.host, StateData#state.nick), - _ = case jlib:iq_query_info(El) of - #iq{xmlns = ?NS_MUC_ADMIN} = IQ -> - iq_admin(StateData, Channel, From, To, IQ); - #iq{xmlns = ?NS_VERSION} -> - Res = io_lib:format("PRIVMSG ~s :\001VERSION\001\r\n", - [Resource]), - _ = ?SEND(Res), - Err = jlib:make_error_reply( - El, ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - #iq{xmlns = ?NS_TIME} -> - Res = io_lib:format("PRIVMSG ~s :\001TIME\001\r\n", - [Resource]), - _ = ?SEND(Res), - Err = jlib:make_error_reply( - El, ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - #iq{xmlns = ?NS_VCARD} -> - Res = io_lib:format("WHOIS ~s \r\n", - [Resource]), - _ = ?SEND(Res), - Err = jlib:make_error_reply( - El, ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - #iq{} -> - Err = jlib:make_error_reply( - El, ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - _ -> - ok - end, - {next_state, StateName, StateData}; - -handle_info({route_chan, _Channel, _Resource, _Packet}, StateName, StateData) -> - {next_state, StateName, StateData}; - - -handle_info({route_nick, Nick, - {xmlelement, "message", Attrs, _Els} = El}, - StateName, StateData) -> - NewStateData = - case xml:get_attr_s("type", Attrs) of - "chat" -> - Body = xml:get_path_s(El, [{elem, "body"}, cdata]), - case Body of - "/quote " ++ Rest -> - ?SEND(Rest ++ "\r\n"); - "/msg " ++ Rest -> - ?SEND("PRIVMSG " ++ Rest ++ "\r\n"); - "/me " ++ Rest -> - Strings = string:tokens(Rest, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format( - "PRIVMSG ~s :\001ACTION ~s\001\r\n", - [Nick, S]) - end, Strings)), - ?SEND(Res); - "/ctcp " ++ Rest -> - Words = string:tokens(Rest, " "), - case Words of - [CtcpDest | _ ] -> - CtcpCmd = toupper(string:substr(Rest, string:str(Rest, " ")+1 )), - Res = io_lib:format( - "PRIVMSG ~s :~s\r\n", - [CtcpDest, "\001" ++ CtcpCmd ++ "\001"]), - ?SEND(Res); - _ -> - ok - end; - _ -> - Strings = string:tokens(Body, "\n"), - Res = lists:concat( - lists:map( - fun(S) -> - io_lib:format("PRIVMSG ~s :~s\r\n", - [Nick, S]) - end, Strings)), - ?SEND(Res) - end; - "error" -> - stop; - _ -> - StateData - end, - if - NewStateData == stop -> - {stop, normal, StateData}; - true -> - {next_state, StateName, NewStateData} - end; - -handle_info({route_nick, _Nick, _Packet}, StateName, StateData) -> - {next_state, StateName, StateData}; - - -handle_info({ircstring, [$P, $I, $N, $G, $ | ID]}, StateName, StateData) -> - send_text(StateData, "PONG " ++ ID ++ "\r\n"), - {next_state, StateName, StateData}; - -handle_info({ircstring, [$: | String]}, wait_for_registration, StateData) -> - Words = string:tokens(String, " "), - {NewState, NewStateData} = - case Words of - [_, "001" | _] -> - {stream_established, StateData}; - [_, "433" | _] -> - {error, - {error, error_nick_in_use(StateData, String), StateData}}; - [_, [$4, _, _] | _] -> - {error, - {error, error_unknown_num(StateData, String, "cancel"), - StateData}}; - [_, [$5, _, _] | _] -> - {error, - {error, error_unknown_num(StateData, String, "cancel"), - StateData}}; - _ -> - ?DEBUG("unknown irc command '~s'~n", [String]), - {wait_for_registration, StateData} - end, - % Note that we don't send any data at this stage. - if - NewState == error -> - {stop, normal, NewStateData}; - true -> - {next_state, NewState, NewStateData} - end; - -handle_info({ircstring, [$: | String]}, _StateName, StateData) -> - Words = string:tokens(String, " "), - NewStateData = - case Words of - [_, "353" | Items] -> - process_channel_list(StateData, Items); - [_, "332", _Nick, [$# | Chan] | _] -> - process_channel_topic(StateData, Chan, String), - StateData; - [_, "333", _Nick, [$# | Chan] | _] -> - process_channel_topic_who(StateData, Chan, String), - StateData; - [_, "318", _, Nick | _] -> - process_endofwhois(StateData, String, Nick), - StateData; - [_, "311", _, Nick, Ident, Irchost | _ ] -> - process_whois311(StateData, String, Nick, Ident, Irchost), - StateData; - [_, "312", _, Nick, Ircserver | _ ] -> - process_whois312(StateData, String, Nick, Ircserver), - StateData; - [_, "319", _, Nick | _ ] -> - process_whois319(StateData, String, Nick), - StateData; - [_, "433" | _] -> - process_nick_in_use(StateData, String); - % CODEPAGE isn't standard, so don't complain if it's not there. - [_, "421", _, "CODEPAGE" | _] -> - StateData; - [_, [$4, _, _] | _] -> - process_num_error(StateData, String); - [_, [$5, _, _] | _] -> - process_num_error(StateData, String); - [From, "PRIVMSG", [$# | Chan] | _] -> - process_chanprivmsg(StateData, Chan, From, String), - StateData; - [From, "NOTICE", [$# | Chan] | _] -> - process_channotice(StateData, Chan, From, String), - StateData; - [From, "PRIVMSG", Nick, ":\001VERSION\001" | _] -> - process_version(StateData, Nick, From), - StateData; - [From, "PRIVMSG", Nick, ":\001USERINFO\001" | _] -> - process_userinfo(StateData, Nick, From), - StateData; - [From, "PRIVMSG", Nick | _] -> - process_privmsg(StateData, Nick, From, String), - StateData; - [From, "NOTICE", Nick | _] -> - process_notice(StateData, Nick, From, String), - StateData; - [From, "TOPIC", [$# | Chan] | _] -> - process_topic(StateData, Chan, From, String), - StateData; - [From, "PART", [$# | Chan] | _] -> - process_part(StateData, Chan, From, String); - [From, "QUIT" | _] -> - process_quit(StateData, From, String); - [From, "JOIN", Chan | _] -> - process_join(StateData, Chan, From, String); - [From, "MODE", [$# | Chan], "+o", Nick | _] -> - process_mode_o(StateData, Chan, From, Nick, - "admin", "moderator"), - StateData; - [From, "MODE", [$# | Chan], "-o", Nick | _] -> - process_mode_o(StateData, Chan, From, Nick, - "member", "participant"), - StateData; - [From, "KICK", [$# | Chan], Nick | _] -> - process_kick(StateData, Chan, From, Nick, String), - StateData; - [From, "NICK", Nick | _] -> - process_nick(StateData, From, Nick); - _ -> - ?DEBUG("unknown irc command '~s'~n", [String]), - StateData - end, - NewStateData1 = - case StateData#state.outbuf of - "" -> - NewStateData; - Data -> - send_text(NewStateData, Data), - NewStateData#state{outbuf = ""} - end, - {next_state, stream_established, NewStateData1}; - -handle_info({ircstring, [$E, $R, $R, $O, $R | _] = String}, - StateName, StateData) -> - process_error(StateData, String), - {next_state, StateName, StateData}; - - -handle_info({ircstring, String}, StateName, StateData) -> - ?DEBUG("unknown irc command '~s'~n", [String]), - {next_state, StateName, StateData}; - - -handle_info({send_text, Text}, StateName, StateData) -> - send_text(StateData, Text), - {next_state, StateName, StateData}; -handle_info({tcp, _Socket, Data}, StateName, StateData) -> - Buf = StateData#state.inbuf ++ binary_to_list(Data), - {ok, Strings} = regexp:split([C || C <- Buf, C /= $\r], "\n"), - ?DEBUG("strings=~p~n", [Strings]), - NewBuf = process_lines(StateData#state.encoding, Strings), - {next_state, StateName, StateData#state{inbuf = NewBuf}}; -handle_info({tcp_closed, _Socket}, StateName, StateData) -> - gen_fsm:send_event(self(), closed), - {next_state, StateName, StateData}; -handle_info({tcp_error, _Socket, _Reason}, StateName, StateData) -> - gen_fsm:send_event(self(), closed), - {next_state, StateName, StateData}. - -%%---------------------------------------------------------------------- -%% Func: terminate/3 -%% Purpose: Shutdown the fsm -%% Returns: any -%%---------------------------------------------------------------------- -terminate(_Reason, _StateName, FullStateData) -> - % Extract error message if there was one. - {Error, StateData} = case FullStateData of - {error, SError, SStateData} -> - {SError, SStateData}; - _ -> - {{xmlelement, "error", [{"code", "502"}], - [{xmlcdata, "Server Connect Failed"}]}, - FullStateData} - end, - mod_irc:closed_connection(StateData#state.host, - StateData#state.user, - StateData#state.server), - bounce_messages("Server Connect Failed"), - lists:foreach( - fun(Chan) -> - Stanza = {xmlelement, "presence", [{"type", "error"}], - [Error]}, - send_stanza(Chan, StateData, Stanza) - end, dict:fetch_keys(StateData#state.channels)), - case StateData#state.socket of - undefined -> - ok; - Socket -> - gen_tcp:close(Socket) - end, - ok. - -send_stanza(Chan, StateData, Stanza) -> - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, StateData#state.nick), - StateData#state.user, - Stanza). - -send_stanza_unavailable(Chan, StateData) -> - Affiliation = "member", % this is a simplification - Role = "none", - Stanza = - {xmlelement, "presence", [{"type", "unavailable"}], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", Affiliation}, - {"role", Role}], - []}, - {xmlelement, "status", - [{"code", "110"}], - []} - ]}]}, - send_stanza(Chan, StateData, Stanza). - -%%%---------------------------------------------------------------------- -%%% Internal functions -%%%---------------------------------------------------------------------- - -send_text(#state{socket = Socket, encoding = Encoding}, Text) -> - CText = iconv:convert("utf-8", Encoding, lists:flatten(Text)), - %?DEBUG("IRC OUTu: ~s~nIRC OUTk: ~s~n", [Text, CText]), - gen_tcp:send(Socket, CText). - - -%send_queue(Socket, Q) -> -% case queue:out(Q) of -% {{value, El}, Q1} -> -% send_element(Socket, El), -% send_queue(Socket, Q1); -% {empty, Q1} -> -% ok -% end. - -bounce_messages(Reason) -> - receive - {send_element, El} -> - {xmlelement, _Name, Attrs, _SubTags} = El, - case xml:get_attr_s("type", Attrs) of - "error" -> - ok; - _ -> - Err = jlib:make_error_reply(El, - "502", Reason), - From = jlib:string_to_jid(xml:get_attr_s("from", Attrs)), - To = jlib:string_to_jid(xml:get_attr_s("to", Attrs)), - ejabberd_router:route(To, From, Err) - end, - bounce_messages(Reason) - after 0 -> - ok - end. - - -route_chan(Pid, Channel, Resource, Packet) -> - Pid ! {route_chan, Channel, Resource, Packet}. - -route_nick(Pid, Nick, Packet) -> - Pid ! {route_nick, Nick, Packet}. - - -process_lines(_Encoding, [S]) -> - S; -process_lines(Encoding, [S | Ss]) -> - self() ! {ircstring, iconv:convert(Encoding, "utf-8", S)}, - process_lines(Encoding, Ss). - -process_channel_list(StateData, Items) -> - process_channel_list_find_chan(StateData, Items). - -process_channel_list_find_chan(StateData, []) -> - StateData; -process_channel_list_find_chan(StateData, [[$# | Chan] | Items]) -> - process_channel_list_users(StateData, Chan, Items); -process_channel_list_find_chan(StateData, [_ | Items]) -> - process_channel_list_find_chan(StateData, Items). - -process_channel_list_users(StateData, _Chan, []) -> - StateData; -process_channel_list_users(StateData, Chan, [User | Items]) -> - NewStateData = process_channel_list_user(StateData, Chan, User), - process_channel_list_users(NewStateData, Chan, Items). - -process_channel_list_user(StateData, Chan, User) -> - User1 = case User of - [$: | U1] -> U1; - _ -> User - end, - {User2, Affiliation, Role} = - case User1 of - [$@ | U2] -> {U2, "admin", "moderator"}; - [$+ | U2] -> {U2, "member", "participant"}; - [$\% | U2] -> {U2, "admin", "moderator"}; - [$& | U2] -> {U2, "admin", "moderator"}; - [$~ | U2] -> {U2, "admin", "moderator"}; - _ -> {User1, "member", "participant"} - end, - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, User2), - StateData#state.user, - {xmlelement, "presence", [], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", Affiliation}, - {"role", Role}], - []}]}]}), - case catch dict:update(Chan, - fun(Ps) -> - ?SETS:add_element(User2, Ps) - end, StateData#state.channels) of - {'EXIT', _} -> - StateData; - NS -> - StateData#state{channels = NS} - end. - - -process_channel_topic(StateData, Chan, String) -> - {ok, Msg, _} = regexp:sub(String, ".*332[^:]*:", ""), - Msg1 = filter_message(Msg), - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "groupchat"}], - [{xmlelement, "subject", [], [{xmlcdata, Msg1}]}, - {xmlelement, "body", [], [{xmlcdata, "Topic for #" ++ Chan ++ ": " ++ Msg1}]} - ]}). - -process_channel_topic_who(StateData, Chan, String) -> - Words = string:tokens(String, " "), - Msg1 = case Words of - [_, "333", _, _Chan, Whoset , Timeset] -> - case string:to_integer(Timeset) of - {Unixtimeset, _Rest} -> - "Topic for #" ++ Chan ++ " set by " ++ Whoset ++ - " at " ++ unixtime2string(Unixtimeset); - _-> - "Topic for #" ++ Chan ++ " set by " ++ Whoset - end; - [_, "333", _, _Chan, Whoset | _] -> - "Topic for #" ++ Chan ++ " set by " ++ Whoset; - _ -> - String - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "groupchat"}], - [{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}). - - -error_nick_in_use(_StateData, String) -> - {ok, Msg, _} = regexp:sub(String, ".*433 +[^ ]* +", ""), - Msg1 = filter_message(Msg), - {xmlelement, "error", [{"code", "409"}, {"type", "cancel"}], - [{xmlelement, "conflict", [{"xmlns", ?NS_STANZAS}], []}, - {xmlelement, "text", [{"xmlns", ?NS_STANZAS}], - [{xmlcdata, Msg1}]}]}. - -process_nick_in_use(StateData, String) -> - % We can't use the jlib macro because we don't know the language of the - % message. - Error = error_nick_in_use(StateData, String), - case StateData#state.nickchannel of - undefined -> - % Shouldn't happen with a well behaved server - StateData; - Chan -> - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, StateData#state.nick), - StateData#state.user, - {xmlelement, "presence", [{"type", "error"}], [Error]}), - StateData#state{nickchannel = undefined} - end. - -process_num_error(StateData, String) -> - Error = error_unknown_num(StateData, String, "continue"), - lists:foreach( - fun(Chan) -> - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, StateData#state.nick), - StateData#state.user, - {xmlelement, "message", [{"type", "error"}], - [Error]}) - end, dict:fetch_keys(StateData#state.channels)), - StateData. - -process_endofwhois(StateData, _String, Nick) -> - ejabberd_router:route( - jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "chat"}], - [{xmlelement, "body", [], [{xmlcdata, "End of WHOIS"}]}]}). - -process_whois311(StateData, String, Nick, Ident, Irchost) -> - {ok, Fullname, _} = regexp:sub(String, ".*311[^:]*:", ""), - ejabberd_router:route( - jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "chat"}], - [{xmlelement, "body", [], - [{xmlcdata, lists:concat( - ["WHOIS: ", Nick, " is ", - Ident, "@" , Irchost, " : " , Fullname])}]}]}). - -process_whois312(StateData, String, Nick, Ircserver) -> - {ok, Ircserverdesc, _} = regexp:sub(String, ".*312[^:]*:", ""), - ejabberd_router:route( - jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "chat"}], - [{xmlelement, "body", [], - [{xmlcdata, lists:concat(["WHOIS: ", Nick, " use ", - Ircserver, " : ", Ircserverdesc])}]}]}). - -process_whois319(StateData, String, Nick) -> - {ok, Chanlist, _} = regexp:sub(String, ".*319[^:]*:", ""), - ejabberd_router:route( - jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "chat"}], - [{xmlelement, "body", [], - [{xmlcdata, lists:concat(["WHOIS: ", Nick, " is on ", - Chanlist])}]}]}). - - - -process_chanprivmsg(StateData, Chan, From, String) -> - [FromUser | _] = string:tokens(From, "!"), - {ok, Msg, _} = regexp:sub(String, ".*PRIVMSG[^:]*:", ""), - Msg1 = case Msg of - [1, $A, $C, $T, $I, $O, $N, $ | Rest] -> - "/me " ++ Rest; - _ -> - Msg - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "message", [{"type", "groupchat"}], - [{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}). - - - -process_channotice(StateData, Chan, From, String) -> - [FromUser | _] = string:tokens(From, "!"), - {ok, Msg, _} = regexp:sub(String, ".*NOTICE[^:]*:", ""), - Msg1 = case Msg of - [1, $A, $C, $T, $I, $O, $N, $ | Rest] -> - "/me " ++ Rest; - _ -> - "/me NOTICE: " ++ Msg - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "message", [{"type", "groupchat"}], - [{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}). - - - - -process_privmsg(StateData, _Nick, From, String) -> - [FromUser | _] = string:tokens(From, "!"), - {ok, Msg, _} = regexp:sub(String, ".*PRIVMSG[^:]*:", ""), - Msg1 = case Msg of - [1, $A, $C, $T, $I, $O, $N, $ | Rest] -> - "/me " ++ Rest; - _ -> - Msg - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route( - jlib:make_jid(lists:concat([FromUser, "!", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "chat"}], - [{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}). - - -process_notice(StateData, _Nick, From, String) -> - [FromUser | _] = string:tokens(From, "!"), - {ok, Msg, _} = regexp:sub(String, ".*NOTICE[^:]*:", ""), - Msg1 = case Msg of - [1, $A, $C, $T, $I, $O, $N, $ | Rest] -> - "/me " ++ Rest; - _ -> - "/me NOTICE: " ++ Msg - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route( - jlib:make_jid(lists:concat([FromUser, "!", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "chat"}], - [{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}). - - -process_version(StateData, _Nick, From) -> - [FromUser | _] = string:tokens(From, "!"), - send_text( - StateData, - io_lib:format("NOTICE ~s :\001VERSION " - "ejabberd IRC transport ~s (c) Alexey Shchepin" - "\001\r\n", - [FromUser, ?VERSION]) ++ - io_lib:format("NOTICE ~s :\001VERSION " - "http://ejabberd.jabberstudio.org/" - "\001\r\n", - [FromUser])). - - -process_userinfo(StateData, _Nick, From) -> - [FromUser | _] = string:tokens(From, "!"), - send_text( - StateData, - io_lib:format("NOTICE ~s :\001USERINFO " - "xmpp:~s" - "\001\r\n", - [FromUser, - jlib:jid_to_string(StateData#state.user)])). - - -process_topic(StateData, Chan, From, String) -> - [FromUser | _] = string:tokens(From, "!"), - {ok, Msg, _} = regexp:sub(String, ".*TOPIC[^:]*:", ""), - Msg1 = filter_message(Msg), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "message", [{"type", "groupchat"}], - [{xmlelement, "subject", [], [{xmlcdata, Msg1}]}, - {xmlelement, "body", [], - [{xmlcdata, "/me has changed the subject to: " ++ - Msg1}]}]}). - -process_part(StateData, Chan, From, String) -> - [FromUser | FromIdent] = string:tokens(From, "!"), - {ok, Msg, _} = regexp:sub(String, ".*PART[^:]*:", ""), - Msg1 = filter_message(Msg), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "presence", [{"type", "unavailable"}], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", "member"}, - {"role", "none"}], - []}]}, - {xmlelement, "status", [], - [{xmlcdata, Msg1 ++ " (" ++ FromIdent ++ ")"}]}] - }), - case catch dict:update(Chan, - fun(Ps) -> - remove_element(FromUser, Ps) - end, StateData#state.channels) of - {'EXIT', _} -> - StateData; - NS -> - StateData#state{channels = NS} - end. - - -process_quit(StateData, From, String) -> - [FromUser | FromIdent] = string:tokens(From, "!"), - - {ok, Msg, _} = regexp:sub(String, ".*QUIT[^:]*:", ""), - Msg1 = filter_message(Msg), - %%NewChans = - dict:map( - fun(Chan, Ps) -> - case ?SETS:is_member(FromUser, Ps) of - true -> - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "presence", [{"type", "unavailable"}], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", "member"}, - {"role", "none"}], - []}]}, - {xmlelement, "status", [], - [{xmlcdata, Msg1 ++ " (" ++ FromIdent ++ ")"}]} - ]}), - remove_element(FromUser, Ps); - _ -> - Ps - end - end, StateData#state.channels), - StateData. - - -process_join(StateData, Channel, From, _String) -> - [FromUser | FromIdent] = string:tokens(From, "!"), - Chan = lists:subtract(Channel, ":#"), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "presence", [], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", "member"}, - {"role", "participant"}], - []}]}, - {xmlelement, "status", [], - [{xmlcdata, FromIdent}]}]}), - - case catch dict:update(Chan, - fun(Ps) -> - ?SETS:add_element(FromUser, Ps) - end, StateData#state.channels) of - {'EXIT', _} -> - StateData; - NS -> - StateData#state{channels = NS} - end. - - - -process_mode_o(StateData, Chan, _From, Nick, Affiliation, Role) -> - %Msg = lists:last(string:tokens(String, ":")), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, Nick), - StateData#state.user, - {xmlelement, "presence", [], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", Affiliation}, - {"role", Role}], - []}]}]}). - -process_kick(StateData, Chan, From, Nick, String) -> - Msg = lists:last(string:tokens(String, ":")), - Msg2 = Nick ++ " kicked by " ++ From ++ " (" ++ filter_message(Msg) ++ ")", - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, ""), - StateData#state.user, - {xmlelement, "message", [{"type", "groupchat"}], - [{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}), - ejabberd_router:route( - jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, Nick), - StateData#state.user, - {xmlelement, "presence", [{"type", "unavailable"}], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", "none"}, - {"role", "none"}], - []}, - {xmlelement, "status", [{"code", "307"}], []} - ]}]}). - -process_nick(StateData, From, NewNick) -> - [FromUser | _] = string:tokens(From, "!"), - Nick = lists:subtract(NewNick, ":"), - NewChans = - dict:map( - fun(Chan, Ps) -> - case ?SETS:is_member(FromUser, Ps) of - true -> - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, FromUser), - StateData#state.user, - {xmlelement, "presence", [{"type", "unavailable"}], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", "member"}, - {"role", "participant"}, - {"nick", Nick}], - []}, - {xmlelement, "status", [{"code", "303"}], []} - ]}]}), - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, Nick), - StateData#state.user, - {xmlelement, "presence", [], - [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], - [{xmlelement, "item", - [{"affiliation", "member"}, - {"role", "participant"}], - []} - ]}]}), - ?SETS:add_element(Nick, - remove_element(FromUser, Ps)); - _ -> - Ps - end - end, StateData#state.channels), - if - FromUser == StateData#state.nick -> - StateData#state{nick = Nick, - nickchannel = undefined, - channels = NewChans}; - true -> - StateData#state{channels = NewChans} - end. - - -process_error(StateData, String) -> - lists:foreach( - fun(Chan) -> - ejabberd_router:route( - jlib:make_jid( - lists:concat([Chan, "%", StateData#state.server]), - StateData#state.host, StateData#state.nick), - StateData#state.user, - {xmlelement, "presence", [{"type", "error"}], - [{xmlelement, "error", [{"code", "502"}], - [{xmlcdata, String}]}]}) - end, dict:fetch_keys(StateData#state.channels)). - -error_unknown_num(_StateData, String, Type) -> - {ok, Msg, _} = regexp:sub(String, ".*[45][0-9][0-9] +[^ ]* +", ""), - Msg1 = filter_message(Msg), - {xmlelement, "error", [{"code", "500"}, {"type", Type}], - [{xmlelement, "undefined-condition", [{"xmlns", ?NS_STANZAS}], []}, - {xmlelement, "text", [{"xmlns", ?NS_STANZAS}], - [{xmlcdata, Msg1}]}]}. - - - -remove_element(E, Set) -> - case ?SETS:is_element(E, Set) of - true -> - ?SETS:del_element(E, Set); - _ -> - Set - end. - - - -iq_admin(StateData, Channel, From, To, - #iq{type = Type, xmlns = XMLNS, sub_el = SubEl} = IQ) -> - case catch process_iq_admin(StateData, Channel, Type, SubEl) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - Res -> - if - Res /= ignore -> - ResIQ = case Res of - {result, ResEls} -> - IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - ResEls - }]}; - {error, Error} -> - IQ#iq{type = error, - sub_el = [SubEl, Error]} - end, - ejabberd_router:route(To, From, - jlib:iq_to_xml(ResIQ)); - true -> - ok - end - end. - - -process_iq_admin(StateData, Channel, set, SubEl) -> - case xml:get_subtag(SubEl, "item") of - false -> - {error, ?ERR_BAD_REQUEST}; - ItemEl -> - Nick = xml:get_tag_attr_s("nick", ItemEl), - Affiliation = xml:get_tag_attr_s("affiliation", ItemEl), - Role = xml:get_tag_attr_s("role", ItemEl), - Reason = xml:get_path_s(ItemEl, [{elem, "reason"}, cdata]), - process_admin(StateData, Channel, Nick, Affiliation, Role, Reason) - end; -process_iq_admin(_StateData, _Channel, get, _SubEl) -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED}. - - - -process_admin(_StateData, _Channel, "", _Affiliation, _Role, _Reason) -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED}; - -process_admin(StateData, Channel, Nick, _Affiliation, "none", Reason) -> - case Reason of - "" -> - send_text(StateData, - io_lib:format("KICK #~s ~s\r\n", - [Channel, Nick])); - _ -> - send_text(StateData, - io_lib:format("KICK #~s ~s :~s\r\n", - [Channel, Nick, Reason])) - end, - {result, []}; - - - -process_admin(_StateData, _Channel, _Nick, _Affiliation, _Role, _Reason) -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED}. - - - -filter_message(Msg) -> - lists:filter( - fun(C) -> - if (C < 32) and - (C /= 9) and - (C /= 10) and - (C /= 13) -> - false; - true -> true - end - end, filter_mirc_colors(Msg)). - -filter_mirc_colors(Msg) -> - case regexp:gsub(Msg, "(\\003[0-9]+)(,[0-9]+)?", "") of - {ok, Msg2, _} -> - Msg2; - _ -> - Msg - end. - -unixtime2string(Unixtime) -> - Secs = Unixtime + calendar:datetime_to_gregorian_seconds( - {{1970, 1, 1}, {0,0,0}}), - case calendar:universal_time_to_local_time( - calendar:gregorian_seconds_to_datetime(Secs)) of - {{Year, Month, Day}, {Hour, Minute, Second}} -> - lists:flatten( - io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second])); - _-> - "0000-00-00 00:00:00" - end. - -toupper([C | Cs]) -> - if - C >= $a, C =< $z -> - [C - 32 | toupper(Cs)]; - true -> - [C | toupper(Cs)] - end; -toupper([]) -> - [].