diff options
Diffstat (limited to 'modules/core')
-rw-r--r-- | modules/core/Makefile.am | 58 | ||||
-rw-r--r-- | modules/core/Makefile.in | 766 | ||||
-rw-r--r-- | modules/core/m_die.c | 97 | ||||
-rw-r--r-- | modules/core/m_error.c | 116 | ||||
-rw-r--r-- | modules/core/m_join.c | 680 | ||||
-rw-r--r-- | modules/core/m_kick.c | 241 | ||||
-rw-r--r-- | modules/core/m_kill.c | 331 | ||||
-rw-r--r-- | modules/core/m_message.c | 935 | ||||
-rw-r--r-- | modules/core/m_mode.c | 313 | ||||
-rw-r--r-- | modules/core/m_nick.c | 1004 | ||||
-rw-r--r-- | modules/core/m_part.c | 167 | ||||
-rw-r--r-- | modules/core/m_quit.c | 98 | ||||
-rw-r--r-- | modules/core/m_server.c | 624 | ||||
-rw-r--r-- | modules/core/m_sjoin.c | 850 | ||||
-rw-r--r-- | modules/core/m_squit.c | 184 |
15 files changed, 6464 insertions, 0 deletions
diff --git a/modules/core/Makefile.am b/modules/core/Makefile.am new file mode 100644 index 0000000..6e4cbba --- /dev/null +++ b/modules/core/Makefile.am @@ -0,0 +1,58 @@ +AUTOMAKE_OPTIONS = foreign +MODULE_FLAGS = -module -avoid-version + +AM_CPPFLAGS = -I$(top_srcdir)/include +modulesdir = $(pkglibdir)/modules + + +m_die_la_LDFLAGS = $(MODULE_FLAGS) +m_error_la_LDFLAGS = $(MODULE_FLAGS) +m_join_la_LDFLAGS = $(MODULE_FLAGS) +m_kick_la_LDFLAGS = $(MODULE_FLAGS) +m_kill_la_LDFLAGS = $(MODULE_FLAGS) +m_message_la_LDFLAGS = $(MODULE_FLAGS) +m_mode_la_LDFLAGS = $(MODULE_FLAGS) +m_nick_la_LDFLAGS = $(MODULE_FLAGS) +m_part_la_LDFLAGS = $(MODULE_FLAGS) +m_quit_la_LDFLAGS = $(MODULE_FLAGS) +m_server_la_LDFLAGS = $(MODULE_FLAGS) +m_sjoin_la_LDFLAGS = $(MODULE_FLAGS) +m_squit_la_LDFLAGS = $(MODULE_FLAGS) + +m_die_la_SOURCES = m_die.c +m_error_la_SOURCES = m_error.c +m_join_la_SOURCES = m_join.c +m_kick_la_SOURCES = m_kick.c +m_kill_la_SOURCES = m_kill.c +m_message_la_SOURCES = m_message.c +m_mode_la_SOURCES = m_mode.c +m_nick_la_SOURCES = m_nick.c +m_part_la_SOURCES = m_part.c +m_quit_la_SOURCES = m_quit.c +m_server_la_SOURCES = m_server.c +m_sjoin_la_SOURCES = m_sjoin.c +m_squit_la_SOURCES = m_squit.c + +modules_LTLIBRARIES = m_die.la \ + m_error.la \ + m_join.la \ + m_kick.la \ + m_kill.la \ + m_message.la \ + m_mode.la \ + m_nick.la \ + m_part.la \ + m_quit.la \ + m_server.la \ + m_sjoin.la \ + m_squit.la + +modules: $(modules_LTLIBRARIES) + +install-exec-hook: + if test -d $(DESTDIR)$(pkglibdir)-old; then \ + rm -rf $(DESTDIR)$(pkglibdir)-old; \ + fi + if test -d $(DESTDIR)$(pkglibdir); then \ + mv $(DESTDIR)$(pkglibdir) $(DESTDIR)$(pkglibdir)-old; \ + fi diff --git a/modules/core/Makefile.in b/modules/core/Makefile.in new file mode 100644 index 0000000..9af88eb --- /dev/null +++ b/modules/core/Makefile.in @@ -0,0 +1,766 @@ +# Makefile.in generated by automake 1.12.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2012 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = modules/core +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(modulesdir)" +LTLIBRARIES = $(modules_LTLIBRARIES) +m_die_la_LIBADD = +am_m_die_la_OBJECTS = m_die.lo +m_die_la_OBJECTS = $(am_m_die_la_OBJECTS) +m_die_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(m_die_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +m_error_la_LIBADD = +am_m_error_la_OBJECTS = m_error.lo +m_error_la_OBJECTS = $(am_m_error_la_OBJECTS) +m_error_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_error_la_LDFLAGS) $(LDFLAGS) -o $@ +m_join_la_LIBADD = +am_m_join_la_OBJECTS = m_join.lo +m_join_la_OBJECTS = $(am_m_join_la_OBJECTS) +m_join_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_join_la_LDFLAGS) $(LDFLAGS) -o $@ +m_kick_la_LIBADD = +am_m_kick_la_OBJECTS = m_kick.lo +m_kick_la_OBJECTS = $(am_m_kick_la_OBJECTS) +m_kick_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_kick_la_LDFLAGS) $(LDFLAGS) -o $@ +m_kill_la_LIBADD = +am_m_kill_la_OBJECTS = m_kill.lo +m_kill_la_OBJECTS = $(am_m_kill_la_OBJECTS) +m_kill_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_kill_la_LDFLAGS) $(LDFLAGS) -o $@ +m_message_la_LIBADD = +am_m_message_la_OBJECTS = m_message.lo +m_message_la_OBJECTS = $(am_m_message_la_OBJECTS) +m_message_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_message_la_LDFLAGS) $(LDFLAGS) -o $@ +m_mode_la_LIBADD = +am_m_mode_la_OBJECTS = m_mode.lo +m_mode_la_OBJECTS = $(am_m_mode_la_OBJECTS) +m_mode_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_mode_la_LDFLAGS) $(LDFLAGS) -o $@ +m_nick_la_LIBADD = +am_m_nick_la_OBJECTS = m_nick.lo +m_nick_la_OBJECTS = $(am_m_nick_la_OBJECTS) +m_nick_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_nick_la_LDFLAGS) $(LDFLAGS) -o $@ +m_part_la_LIBADD = +am_m_part_la_OBJECTS = m_part.lo +m_part_la_OBJECTS = $(am_m_part_la_OBJECTS) +m_part_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_part_la_LDFLAGS) $(LDFLAGS) -o $@ +m_quit_la_LIBADD = +am_m_quit_la_OBJECTS = m_quit.lo +m_quit_la_OBJECTS = $(am_m_quit_la_OBJECTS) +m_quit_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_quit_la_LDFLAGS) $(LDFLAGS) -o $@ +m_server_la_LIBADD = +am_m_server_la_OBJECTS = m_server.lo +m_server_la_OBJECTS = $(am_m_server_la_OBJECTS) +m_server_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_server_la_LDFLAGS) $(LDFLAGS) -o $@ +m_sjoin_la_LIBADD = +am_m_sjoin_la_OBJECTS = m_sjoin.lo +m_sjoin_la_OBJECTS = $(am_m_sjoin_la_OBJECTS) +m_sjoin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_sjoin_la_LDFLAGS) $(LDFLAGS) -o $@ +m_squit_la_LIBADD = +am_m_squit_la_OBJECTS = m_squit.lo +m_squit_la_OBJECTS = $(am_m_squit_la_OBJECTS) +m_squit_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(m_squit_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(m_die_la_SOURCES) $(m_error_la_SOURCES) \ + $(m_join_la_SOURCES) $(m_kick_la_SOURCES) $(m_kill_la_SOURCES) \ + $(m_message_la_SOURCES) $(m_mode_la_SOURCES) \ + $(m_nick_la_SOURCES) $(m_part_la_SOURCES) $(m_quit_la_SOURCES) \ + $(m_server_la_SOURCES) $(m_sjoin_la_SOURCES) \ + $(m_squit_la_SOURCES) +DIST_SOURCES = $(m_die_la_SOURCES) $(m_error_la_SOURCES) \ + $(m_join_la_SOURCES) $(m_kick_la_SOURCES) $(m_kill_la_SOURCES) \ + $(m_message_la_SOURCES) $(m_mode_la_SOURCES) \ + $(m_nick_la_SOURCES) $(m_part_la_SOURCES) $(m_quit_la_SOURCES) \ + $(m_server_la_SOURCES) $(m_sjoin_la_SOURCES) \ + $(m_squit_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +ARGZ_H = @ARGZ_H@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIR = @DATADIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INCLTDL = @INCLTDL@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBADD_DL = @LIBADD_DL@ +LIBADD_DLD_LINK = @LIBADD_DLD_LINK@ +LIBADD_DLOPEN = @LIBADD_DLOPEN@ +LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@ +LIBDIR = @LIBDIR@ +LIBLTDL = @LIBLTDL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALSTATEDIR = @LOCALSTATEDIR@ +LTDLDEPS = @LTDLDEPS@ +LTDLINCL = @LTDLINCL@ +LTDLOPEN = @LTDLOPEN@ +LTLIBOBJS = @LTLIBOBJS@ +LT_CONFIG_H = @LT_CONFIG_H@ +LT_DLLOADERS = @LT_DLLOADERS@ +LT_DLPREOPEN = @LT_DLPREOPEN@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PREFIX = @PREFIX@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSCONFDIR = @SYSCONFDIR@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +ltdl_LIBOBJS = @ltdl_LIBOBJS@ +ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sys_symbol_underscore = @sys_symbol_underscore@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +MODULE_FLAGS = -module -avoid-version +AM_CPPFLAGS = -I$(top_srcdir)/include +modulesdir = $(pkglibdir)/modules +m_die_la_LDFLAGS = $(MODULE_FLAGS) +m_error_la_LDFLAGS = $(MODULE_FLAGS) +m_join_la_LDFLAGS = $(MODULE_FLAGS) +m_kick_la_LDFLAGS = $(MODULE_FLAGS) +m_kill_la_LDFLAGS = $(MODULE_FLAGS) +m_message_la_LDFLAGS = $(MODULE_FLAGS) +m_mode_la_LDFLAGS = $(MODULE_FLAGS) +m_nick_la_LDFLAGS = $(MODULE_FLAGS) +m_part_la_LDFLAGS = $(MODULE_FLAGS) +m_quit_la_LDFLAGS = $(MODULE_FLAGS) +m_server_la_LDFLAGS = $(MODULE_FLAGS) +m_sjoin_la_LDFLAGS = $(MODULE_FLAGS) +m_squit_la_LDFLAGS = $(MODULE_FLAGS) +m_die_la_SOURCES = m_die.c +m_error_la_SOURCES = m_error.c +m_join_la_SOURCES = m_join.c +m_kick_la_SOURCES = m_kick.c +m_kill_la_SOURCES = m_kill.c +m_message_la_SOURCES = m_message.c +m_mode_la_SOURCES = m_mode.c +m_nick_la_SOURCES = m_nick.c +m_part_la_SOURCES = m_part.c +m_quit_la_SOURCES = m_quit.c +m_server_la_SOURCES = m_server.c +m_sjoin_la_SOURCES = m_sjoin.c +m_squit_la_SOURCES = m_squit.c +modules_LTLIBRARIES = m_die.la \ + m_error.la \ + m_join.la \ + m_kick.la \ + m_kill.la \ + m_message.la \ + m_mode.la \ + m_nick.la \ + m_part.la \ + m_quit.la \ + m_server.la \ + m_sjoin.la \ + m_squit.la + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign modules/core/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign modules/core/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-modulesLTLIBRARIES: $(modules_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(modules_LTLIBRARIES)'; test -n "$(modulesdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(modulesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(modulesdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(modulesdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(modulesdir)"; \ + } + +uninstall-modulesLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(modules_LTLIBRARIES)'; test -n "$(modulesdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(modulesdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(modulesdir)/$$f"; \ + done + +clean-modulesLTLIBRARIES: + -test -z "$(modules_LTLIBRARIES)" || rm -f $(modules_LTLIBRARIES) + @list='$(modules_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +m_die.la: $(m_die_la_OBJECTS) $(m_die_la_DEPENDENCIES) $(EXTRA_m_die_la_DEPENDENCIES) + $(m_die_la_LINK) -rpath $(modulesdir) $(m_die_la_OBJECTS) $(m_die_la_LIBADD) $(LIBS) +m_error.la: $(m_error_la_OBJECTS) $(m_error_la_DEPENDENCIES) $(EXTRA_m_error_la_DEPENDENCIES) + $(m_error_la_LINK) -rpath $(modulesdir) $(m_error_la_OBJECTS) $(m_error_la_LIBADD) $(LIBS) +m_join.la: $(m_join_la_OBJECTS) $(m_join_la_DEPENDENCIES) $(EXTRA_m_join_la_DEPENDENCIES) + $(m_join_la_LINK) -rpath $(modulesdir) $(m_join_la_OBJECTS) $(m_join_la_LIBADD) $(LIBS) +m_kick.la: $(m_kick_la_OBJECTS) $(m_kick_la_DEPENDENCIES) $(EXTRA_m_kick_la_DEPENDENCIES) + $(m_kick_la_LINK) -rpath $(modulesdir) $(m_kick_la_OBJECTS) $(m_kick_la_LIBADD) $(LIBS) +m_kill.la: $(m_kill_la_OBJECTS) $(m_kill_la_DEPENDENCIES) $(EXTRA_m_kill_la_DEPENDENCIES) + $(m_kill_la_LINK) -rpath $(modulesdir) $(m_kill_la_OBJECTS) $(m_kill_la_LIBADD) $(LIBS) +m_message.la: $(m_message_la_OBJECTS) $(m_message_la_DEPENDENCIES) $(EXTRA_m_message_la_DEPENDENCIES) + $(m_message_la_LINK) -rpath $(modulesdir) $(m_message_la_OBJECTS) $(m_message_la_LIBADD) $(LIBS) +m_mode.la: $(m_mode_la_OBJECTS) $(m_mode_la_DEPENDENCIES) $(EXTRA_m_mode_la_DEPENDENCIES) + $(m_mode_la_LINK) -rpath $(modulesdir) $(m_mode_la_OBJECTS) $(m_mode_la_LIBADD) $(LIBS) +m_nick.la: $(m_nick_la_OBJECTS) $(m_nick_la_DEPENDENCIES) $(EXTRA_m_nick_la_DEPENDENCIES) + $(m_nick_la_LINK) -rpath $(modulesdir) $(m_nick_la_OBJECTS) $(m_nick_la_LIBADD) $(LIBS) +m_part.la: $(m_part_la_OBJECTS) $(m_part_la_DEPENDENCIES) $(EXTRA_m_part_la_DEPENDENCIES) + $(m_part_la_LINK) -rpath $(modulesdir) $(m_part_la_OBJECTS) $(m_part_la_LIBADD) $(LIBS) +m_quit.la: $(m_quit_la_OBJECTS) $(m_quit_la_DEPENDENCIES) $(EXTRA_m_quit_la_DEPENDENCIES) + $(m_quit_la_LINK) -rpath $(modulesdir) $(m_quit_la_OBJECTS) $(m_quit_la_LIBADD) $(LIBS) +m_server.la: $(m_server_la_OBJECTS) $(m_server_la_DEPENDENCIES) $(EXTRA_m_server_la_DEPENDENCIES) + $(m_server_la_LINK) -rpath $(modulesdir) $(m_server_la_OBJECTS) $(m_server_la_LIBADD) $(LIBS) +m_sjoin.la: $(m_sjoin_la_OBJECTS) $(m_sjoin_la_DEPENDENCIES) $(EXTRA_m_sjoin_la_DEPENDENCIES) + $(m_sjoin_la_LINK) -rpath $(modulesdir) $(m_sjoin_la_OBJECTS) $(m_sjoin_la_LIBADD) $(LIBS) +m_squit.la: $(m_squit_la_OBJECTS) $(m_squit_la_DEPENDENCIES) $(EXTRA_m_squit_la_DEPENDENCIES) + $(m_squit_la_LINK) -rpath $(modulesdir) $(m_squit_la_OBJECTS) $(m_squit_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_die.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_join.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_kick.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_kill.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_message.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_mode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_nick.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_part.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_quit.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_server.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_sjoin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_squit.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +cscopelist: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(modulesdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-modulesLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-modulesLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-modulesLTLIBRARIES + +.MAKE: install-am install-exec-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-modulesLTLIBRARIES cscopelist ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-hook install-html install-html-am \ + install-info install-info-am install-man \ + install-modulesLTLIBRARIES install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-modulesLTLIBRARIES + + +modules: $(modules_LTLIBRARIES) + +install-exec-hook: + if test -d $(DESTDIR)$(pkglibdir)-old; then \ + rm -rf $(DESTDIR)$(pkglibdir)-old; \ + fi + if test -d $(DESTDIR)$(pkglibdir); then \ + mv $(DESTDIR)$(pkglibdir) $(DESTDIR)$(pkglibdir)-old; \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/modules/core/m_die.c b/modules/core/m_die.c new file mode 100644 index 0000000..b47c92c --- /dev/null +++ b/modules/core/m_die.c @@ -0,0 +1,97 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_die.c: Kills off this server. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "client.h" +#include "ircd.h" +#include "irc_string.h" +#include "numeric.h" +#include "send.h" +#include "parse.h" +#include "modules.h" +#include "restart.h" +#include "conf.h" + + +/* + * mo_die - DIE command handler + */ +static void +mo_die(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char buf[IRCD_BUFSIZE]; + + if (!HasOFlag(source_p, OPER_FLAG_DIE)) + { + sendto_one(source_p, form_str(ERR_NOPRIVS), + me.name, source_p->name, "die"); + return; + } + + if (parc < 2 || EmptyString(parv[1])) + { + sendto_one(source_p, ":%s NOTICE %s :Need server name /die %s", + me.name, source_p->name, me.name); + return; + } + + if (irccmp(parv[1], me.name)) + { + sendto_one(source_p, ":%s NOTICE %s :Mismatch on /die %s", + me.name, source_p->name, me.name); + return; + } + + snprintf(buf, sizeof(buf), "received DIE command from %s", + get_oper_name(source_p)); + server_die(buf, 0); +} + +static struct Message die_msgtab = { + "DIE", 0, 0, 1, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_not_oper, m_ignore, m_ignore, mo_die, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&die_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&die_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_error.c b/modules/core/m_error.c new file mode 100644 index 0000000..9228628 --- /dev/null +++ b/modules/core/m_error.c @@ -0,0 +1,116 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_error.c: Handles error messages from the other end. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "client.h" +#include "ircd.h" +#include "send.h" +#include "modules.h" +#include "log.h" +#include "parse.h" + + +/* + * Note: At least at protocol level ERROR has only one parameter. + * --msa + * + * parv[0] = sender prefix + * parv[*] = parameters + */ +static void +m_error(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + const char *para; + + para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>"; + + ilog(LOG_TYPE_IRCD, "Received ERROR message from %s: %s", + source_p->name, para); + + if (client_p == source_p) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, "ERROR :from %s -- %s", + get_client_name(client_p, HIDE_IP), para); + sendto_realops_flags(UMODE_ALL, L_OPER, "ERROR :from %s -- %s", + get_client_name(client_p, MASK_IP), para); + } + else + { + sendto_realops_flags(UMODE_ALL, L_OPER, "ERROR :from %s via %s -- %s", + source_p->name, get_client_name(client_p, MASK_IP), para); + sendto_realops_flags(UMODE_ALL, L_ADMIN, "ERROR :from %s via %s -- %s", + source_p->name, get_client_name(client_p, HIDE_IP), para); + } + + if (MyClient(source_p)) + exit_client(source_p, source_p, "ERROR"); +} + +static void +ms_error(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + const char *para; + + para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>"; + + ilog(LOG_TYPE_IRCD, "Received ERROR message from %s: %s", + source_p->name, para); + + if (client_p == source_p) + sendto_realops_flags(UMODE_ALL, L_ALL, "ERROR :from %s -- %s", + get_client_name(client_p, MASK_IP), para); + else + sendto_realops_flags(UMODE_ALL, L_ALL, "ERROR :from %s via %s -- %s", + source_p->name, + get_client_name(client_p, MASK_IP), para); +} + +static struct Message error_msgtab = { + "ERROR", 0, 0, 1, MAXPARA, MFLG_SLOW, 0, + { m_error, m_ignore, ms_error, m_ignore, m_ignore, m_ignore } +}; + +static void +module_init(void) +{ + mod_add_cmd(&error_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&error_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_join.c b/modules/core/m_join.c new file mode 100644 index 0000000..35bbf3a --- /dev/null +++ b/modules/core/m_join.c @@ -0,0 +1,680 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_join.c: Joins a channel. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "hash.h" +#include "irc_string.h" +#include "sprintf_irc.h" +#include "ircd.h" +#include "numeric.h" +#include "send.h" +#include "s_serv.h" +#include "conf.h" +#include "parse.h" +#include "modules.h" + + +static void do_join_0(struct Client *, struct Client *); + +static void set_final_mode(struct Mode *, struct Mode *); +static void remove_our_modes(struct Channel *, struct Client *); +static void remove_a_mode(struct Channel *, struct Client *, int, char); + +static char modebuf[MODEBUFLEN]; +static char parabuf[MODEBUFLEN]; +static char sendbuf[MODEBUFLEN]; +static char *mbuf; + +/* last0() stolen from ircu */ +static char * +last0(struct Client *client_p, struct Client *source_p, char *chanlist) +{ + char *p; + int join0 = 0; + + for (p = chanlist; *p; ++p) /* find last "JOIN 0" */ + { + if (*p == '0' && (*(p + 1) == ',' || *(p + 1) == '\0')) + { + if ((*p + 1) == ',') + ++p; + + chanlist = p + 1; + join0 = 1; + } + else + { + while (*p != ',' && *p != '\0') /* skip past channel name */ + ++p; + + if (*p == '\0') /* hit the end */ + break; + } + } + + if (join0) + do_join_0(client_p, source_p); + + return chanlist; +} + +/* m_join() + * parv[0] = sender prefix + * parv[1] = channel + * parv[2] = channel password (key) + */ +static void +m_join(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char *p = NULL; + char *key_list = NULL; + char *chan_list = NULL; + char *chan = NULL; + struct Channel *chptr = NULL; + int i = 0; + unsigned int flags = 0; + + if (EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "JOIN"); + return; + } + + assert(client_p == source_p); + + key_list = parv[2]; + chan_list = last0(client_p, source_p, parv[1]); + + for (chan = strtoken(&p, chan_list, ","); chan; + chan = strtoken(&p, NULL, ",")) + { + char *key = NULL; + + /* If we have any more keys, take the first for this channel. */ + if (!EmptyString(key_list) && (key_list = strchr(key = key_list, ','))) + *key_list++ = '\0'; + + /* Empty keys are the same as no keys. */ + if (key && *key == '\0') + key = NULL; + + if (!check_channel_name(chan, 1)) + { + sendto_one(source_p, form_str(ERR_BADCHANNAME), + me.name, source_p->name, chan); + continue; + } + + if (!IsExemptResv(source_p) && + !(HasUMode(source_p, UMODE_OPER) && ConfigFileEntry.oper_pass_resv) && + (!hash_find_resv(chan) == ConfigChannel.restrict_channels)) + { + sendto_one(source_p, form_str(ERR_BADCHANNAME), + me.name, source_p->name, chan); + sendto_realops_flags(UMODE_SPY, L_ALL, + "Forbidding reserved channel [%s] from user %s", + chan, get_client_name(source_p, HIDE_IP)); + continue; + } + + if (dlink_list_length(&source_p->channel) >= + (HasUMode(source_p, UMODE_OPER) ? + ConfigChannel.max_chans_per_oper : + ConfigChannel.max_chans_per_user)) + { + sendto_one(source_p, form_str(ERR_TOOMANYCHANNELS), + me.name, source_p->name, chan); + break; + } + + if ((chptr = hash_find_channel(chan)) != NULL) + { + if (IsMember(source_p, chptr)) + continue; + + if (splitmode && !HasUMode(source_p, UMODE_OPER) && + ConfigChannel.no_join_on_split) + { + sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), + me.name, source_p->name, chan); + continue; + } + + /* + * can_join checks for +i key, bans. + */ + if ((i = can_join(source_p, chptr, key))) + { + sendto_one(source_p, form_str(i), me.name, + source_p->name, chptr->chname); + continue; + } + + /* + * This should never be the case unless there is some sort of + * persistant channels. + */ + if (dlink_list_length(&chptr->members) == 0) + flags = CHFL_CHANOP; + else + flags = 0; + } + else + { + if (splitmode && !HasUMode(source_p, UMODE_OPER) && + (ConfigChannel.no_create_on_split || ConfigChannel.no_join_on_split)) + { + sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), + me.name, source_p->name, chan); + continue; + } + + flags = CHFL_CHANOP; + chptr = make_channel(chan); + } + + if (!HasUMode(source_p, UMODE_OPER)) + check_spambot_warning(source_p, chptr->chname); + + add_user_to_channel(chptr, source_p, flags, 1); + + /* + * Set timestamp if appropriate, and propagate + */ + if (flags & CHFL_CHANOP) + { + chptr->channelts = CurrentTime; + chptr->mode.mode |= MODE_TOPICLIMIT; + chptr->mode.mode |= MODE_NOPRIVMSGS; + + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s SJOIN %lu %s +nt :@%s", + me.id, (unsigned long)chptr->channelts, + chptr->chname, source_p->id); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s SJOIN %lu %s +nt :@%s", + me.name, (unsigned long)chptr->channelts, + chptr->chname, source_p->name); + /* + * notify all other users on the new channel + */ + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s JOIN :%s", + source_p->name, source_p->username, + source_p->host, chptr->chname); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s +nt", + me.name, chptr->chname); + } + else + { + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s JOIN %lu %s +", + source_p->id, (unsigned long)chptr->channelts, + chptr->chname); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s SJOIN %lu %s + :%s", + me.name, (unsigned long)chptr->channelts, + chptr->chname, source_p->name); + + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s JOIN :%s", + source_p->name, source_p->username, + source_p->host, chptr->chname); + } + + del_invite(chptr, source_p); + + if (chptr->topic[0]) + { + sendto_one(source_p, form_str(RPL_TOPIC), me.name, + source_p->name, chptr->chname, chptr->topic); + + sendto_one(source_p, form_str(RPL_TOPICWHOTIME), + me.name, source_p->name, chptr->chname, + chptr->topic_info, chptr->topic_time); + } + + channel_member_names(source_p, chptr, 1); + + source_p->localClient->last_join_time = CurrentTime; + } +} + +/* ms_join() + * + * inputs - parv[0] = uid + * parv[1] = ts + * parv[2] = channel name + * parv[3] = modes (Deprecated) + * output - none + * side effects - handles remote JOIN's sent by servers. In TSora + * remote clients are joined using SJOIN, hence a + * JOIN sent by a server on behalf of a client is an error. + * here, the initial code is in to take an extra parameter + * and use it for the TimeStamp on a new channel. + */ +static void +ms_join(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + time_t newts = 0; + time_t oldts = 0; + int keep_our_modes = 1; + int keep_new_modes = 1; + int isnew = 0; + const char *servername = NULL; + struct Channel *chptr = NULL; + struct Mode mode, *oldmode; + + if (parc == 2 && !irccmp(parv[1], "0")) + { + do_join_0(client_p, source_p); + return; + } + + if (parc < 4) + return; + + if (!check_channel_name(parv[2], 0)) + { + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "*** Too long or invalid channel name from %s: %s", + client_p->name, parv[2]); + return; + } + + mbuf = modebuf; + mode.mode = mode.limit = 0; + mode.key[0] = '\0'; + + if ((chptr = hash_find_channel(parv[2])) == NULL) + { + isnew = 1; + chptr = make_channel(parv[2]); + } + + newts = atol(parv[1]); + oldts = chptr->channelts; + oldmode = &chptr->mode; + + if (ConfigFileEntry.ignore_bogus_ts) + { + if (newts < 800000000) + { + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "*** Bogus TS %lu on %s ignored from %s", + (unsigned long)newts, chptr->chname, + client_p->name); + + newts = (oldts == 0) ? 0 : 800000000; + } + } + else + { + if (!newts && !isnew && oldts) + { + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to 0", + me.name, chptr->chname, chptr->chname, (unsigned long)oldts); + sendto_realops_flags(UMODE_ALL, L_ALL, + "Server %s changing TS on %s from %lu to 0", + source_p->name, chptr->chname, (unsigned long)oldts); + } + } + + if (isnew) + chptr->channelts = newts; + else if (newts == 0 || oldts == 0) + chptr->channelts = 0; + else if (newts == oldts) + ; + else if (newts < oldts) + { + keep_our_modes = 0; + chptr->channelts = newts; + } + else + keep_new_modes = 0; + + if (!keep_new_modes) + mode = *oldmode; + else if (keep_our_modes) + { + mode.mode |= oldmode->mode; + if (oldmode->limit > mode.limit) + mode.limit = oldmode->limit; + if (strcmp(mode.key, oldmode->key) < 0) + strcpy(mode.key, oldmode->key); + } + + set_final_mode(&mode, oldmode); + chptr->mode = mode; + + /* Lost the TS, other side wins, so remove modes on this side */ + if (!keep_our_modes) + { + remove_our_modes(chptr, source_p); + + if (chptr->topic[0]) + { + set_channel_topic(chptr, "", "", 0); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s TOPIC %s :", + (IsHidden(source_p) || + ConfigServerHide.hide_servers) ? + me.name : source_p->name, chptr->chname); + } + + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to %lu", + me.name, chptr->chname, chptr->chname, + (unsigned long)oldts, (unsigned long)newts); + } + + if (*modebuf != '\0') + { + servername = (ConfigServerHide.hide_servers || IsHidden(source_p)) ? + me.name : source_p->name; + + /* This _SHOULD_ be to ALL_MEMBERS + * It contains only +imnpstlk, etc */ + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s %s %s", + servername, chptr->chname, modebuf, parabuf); + } + + if (!IsMember(source_p, chptr)) + { + add_user_to_channel(chptr, source_p, 0, 1); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s JOIN :%s", + source_p->name, source_p->username, + source_p->host, chptr->chname); + } + + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s JOIN %lu %s +", + ID(source_p), (unsigned long)chptr->channelts, chptr->chname); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s SJOIN %lu %s + :%s", + source_p->servptr->name, (unsigned long)chptr->channelts, + chptr->chname, source_p->name); +} + +/* do_join_0() + * + * inputs - pointer to client doing join 0 + * output - NONE + * side effects - Use has decided to join 0. This is legacy + * from the days when channels were numbers not names. *sigh* + * There is a bunch of evilness necessary here due to + * anti spambot code. + */ +static void +do_join_0(struct Client *client_p, struct Client *source_p) +{ + struct Channel *chptr = NULL; + dlink_node *ptr = NULL, *ptr_next = NULL; + + if (source_p->channel.head) + if (MyConnect(source_p) && !HasUMode(source_p, UMODE_OPER)) + check_spambot_warning(source_p, NULL); + + DLINK_FOREACH_SAFE(ptr, ptr_next, source_p->channel.head) + { + chptr = ((struct Membership *)ptr->data)->chptr; + + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s PART %s", ID(source_p), chptr->chname); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s PART %s", source_p->name, chptr->chname); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s PART %s", + source_p->name, source_p->username, + source_p->host, chptr->chname); + + remove_user_from_channel(ptr->data); + } +} + +/* set_final_mode + * + * inputs - channel mode + * - old channel mode + * output - NONE + * side effects - walk through all the channel modes turning off modes + * that were on in oldmode but aren't on in mode. + * Then walk through turning on modes that are on in mode + * but were not set in oldmode. + */ +static void +set_final_mode(struct Mode *mode, struct Mode *oldmode) +{ + const struct mode_letter *tab; + char *pbuf = parabuf; + int what = 0; + int len; + + for (tab = chan_modes; tab->letter; ++tab) + { + if ((tab->mode & mode->mode) && + !(tab->mode & oldmode->mode)) + { + if (what != 1) + { + *mbuf++ = '+'; + what = 1; + } + *mbuf++ = tab->letter; + } + } + + for (tab = chan_modes; tab->letter; ++tab) + { + if ((tab->mode & oldmode->mode) && + !(tab->mode & mode->mode)) + { + if (what != -1) + { + *mbuf++ = '-'; + what = -1; + } + *mbuf++ = tab->letter; + } + } + + if (oldmode->limit != 0 && mode->limit == 0) + { + if (what != -1) + { + *mbuf++ = '-'; + what = -1; + } + *mbuf++ = 'l'; + } + + if (oldmode->key[0] && !mode->key[0]) + { + if (what != -1) + { + *mbuf++ = '-'; + what = -1; + } + *mbuf++ = 'k'; + len = ircsprintf(pbuf, "%s ", oldmode->key); + pbuf += len; + } + + if (mode->limit != 0 && oldmode->limit != mode->limit) + { + if (what != 1) + { + *mbuf++ = '+'; + what = 1; + } + *mbuf++ = 'l'; + len = ircsprintf(pbuf, "%d ", mode->limit); + pbuf += len; + } + + if (mode->key[0] && strcmp(oldmode->key, mode->key)) + { + if (what != 1) + { + *mbuf++ = '+'; + what = 1; + } + *mbuf++ = 'k'; + len = ircsprintf(pbuf, "%s ", mode->key); + pbuf += len; + } + *mbuf = '\0'; +} + +/* remove_our_modes() + * + * inputs - pointer to channel to remove modes from + * - client pointer + * output - NONE + * side effects - Go through the local members, remove all their + * chanop modes etc., this side lost the TS. + */ +static void +remove_our_modes(struct Channel *chptr, struct Client *source_p) +{ + remove_a_mode(chptr, source_p, CHFL_CHANOP, 'o'); +#ifdef HALFOPS + remove_a_mode(chptr, source_p, CHFL_HALFOP, 'h'); +#endif + remove_a_mode(chptr, source_p, CHFL_VOICE, 'v'); +} + +/* remove_a_mode() + * + * inputs - + * output - NONE + * side effects - remove ONE mode from a channel + */ +static void +remove_a_mode(struct Channel *chptr, struct Client *source_p, + int mask, char flag) +{ + dlink_node *ptr; + struct Membership *ms; + char lmodebuf[MODEBUFLEN]; + const char *lpara[MAXMODEPARAMS]; + int count = 0; + int lcount; + + mbuf = lmodebuf; + *mbuf++ = '-'; + + for (lcount = 0; lcount < MAXMODEPARAMS; lcount++) + lpara[lcount] = ""; + sendbuf[0] = '\0'; + + DLINK_FOREACH(ptr, chptr->members.head) + { + ms = ptr->data; + + if ((ms->flags & mask) == 0) + continue; + + ms->flags &= ~mask; + + lpara[count++] = ms->client_p->name; + + *mbuf++ = flag; + + if (count >= MAXMODEPARAMS) + { + for (lcount = 0; lcount < MAXMODEPARAMS; lcount++) + { + if (*lpara[lcount] == '\0') + break; + + strlcat(sendbuf, " ", sizeof(sendbuf)); + strlcat(sendbuf, lpara[lcount], sizeof(sendbuf)); + lpara[lcount] = ""; + } + + *mbuf = '\0'; + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s MODE %s %s%s", + (IsHidden(source_p) || + ConfigServerHide.hide_servers) ? + me.name : source_p->name, + chptr->chname, lmodebuf, sendbuf); + mbuf = lmodebuf; + *mbuf++ = '-'; + count = 0; + sendbuf[0] = '\0'; + } + } + + if (count != 0) + { + *mbuf = '\0'; + for (lcount = 0; lcount < MAXMODEPARAMS; lcount++) + { + if (*lpara[lcount] == '\0') + break; + + strlcat(sendbuf, " ", sizeof(sendbuf)); + strlcat(sendbuf, lpara[lcount], sizeof(sendbuf)); + } + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s MODE %s %s%s", + (IsHidden(source_p) || ConfigServerHide.hide_servers) ? + me.name : source_p->name, + chptr->chname, lmodebuf, sendbuf); + } +} + +static struct Message join_msgtab = { + "JOIN", 0, 0, 2, MAXPARA, MFLG_SLOW, 0, + { m_unregistered, m_join, ms_join, m_ignore, m_join, m_ignore } +}; + +static void +module_init(void) +{ + mod_add_cmd(&join_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&join_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_kick.c b/modules/core/m_kick.c new file mode 100644 index 0000000..f800136 --- /dev/null +++ b/modules/core/m_kick.c @@ -0,0 +1,241 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_kick.c: Kicks a user from a channel. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "irc_string.h" +#include "ircd.h" +#include "numeric.h" +#include "send.h" +#include "modules.h" +#include "parse.h" +#include "hash.h" +#include "packet.h" +#include "s_serv.h" + + +/* m_kick() + * parv[0] = sender prefix + * parv[1] = channel + * parv[2] = client to kick + * parv[3] = kick comment + */ +static void +m_kick(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *who; + struct Channel *chptr; + int chasing = 0; + char *comment; + char *name; + char *p = NULL; + char *user; + const char *from, *to; + struct Membership *ms = NULL; + struct Membership *ms_target; + + if (!MyConnect(source_p) && IsCapable(source_p->from, CAP_TS6) && HasID(source_p)) + { + from = me.id; + to = source_p->id; + } + else + { + from = me.name; + to = source_p->name; + } + + if (EmptyString(parv[2])) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + from, to, "KICK"); + return; + } + + if (MyClient(source_p) && !IsFloodDone(source_p)) + flood_endgrace(source_p); + + comment = (EmptyString(parv[3])) ? parv[2] : parv[3]; + if (strlen(comment) > (size_t)KICKLEN) + comment[KICKLEN] = '\0'; + + name = parv[1]; + while (*name == ',') + name++; + + if ((p = strchr(name,',')) != NULL) + *p = '\0'; + if (*name == '\0') + return; + + if ((chptr = hash_find_channel(name)) == NULL) + { + sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), + from, to, name); + return; + } + + if (!IsServer(source_p) && !HasFlag(source_p, FLAGS_SERVICE)) + { + if ((ms = find_channel_link(source_p, chptr)) == NULL) + { + if (MyConnect(source_p)) + { + sendto_one(source_p, form_str(ERR_NOTONCHANNEL), + me.name, source_p->name, name); + return; + } + } + + if (!has_member_flags(ms, CHFL_CHANOP|CHFL_HALFOP)) + { + /* was a user, not a server, and user isn't seen as a chanop here */ + if (MyConnect(source_p)) + { + /* user on _my_ server, with no chanops.. so go away */ + sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), + me.name, source_p->name, name); + return; + } + + if (chptr->channelts == 0) + { + /* If its a TS 0 channel, do it the old way */ + sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), + from, to, name); + return; + } + + /* Its a user doing a kick, but is not showing as chanop locally + * its also not a user ON -my- server, and the channel has a TS. + * There are two cases we can get to this point then... + * + * 1) connect burst is happening, and for some reason a legit + * op has sent a KICK, but the SJOIN hasn't happened yet or + * been seen. (who knows.. due to lag...) + * + * 2) The channel is desynced. That can STILL happen with TS + * + * Now, the old code roger wrote, would allow the KICK to + * go through. Thats quite legit, but lets weird things like + * KICKS by users who appear not to be chanopped happen, + * or even neater, they appear not to be on the channel. + * This fits every definition of a desync, doesn't it? ;-) + * So I will allow the KICK, otherwise, things are MUCH worse. + * But I will warn it as a possible desync. + * + * -Dianora + */ + } + } + + user = parv[2]; + + while (*user == ',') + user++; + + if ((p = strchr(user, ',')) != NULL) + *p = '\0'; + + if (*user == '\0') + return; + + if ((who = find_chasing(client_p, source_p, user, &chasing)) == NULL) + return; + + if ((ms_target = find_channel_link(who, chptr)) != NULL) + { +#ifdef HALFOPS + /* half ops cannot kick other halfops on private channels */ + if (has_member_flags(ms, CHFL_HALFOP) && !has_member_flags(ms, CHFL_CHANOP)) + { + if (((chptr->mode.mode & MODE_PRIVATE) && has_member_flags(ms_target, + CHFL_CHANOP|CHFL_HALFOP)) || has_member_flags(ms_target, CHFL_CHANOP)) + { + sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), + me.name, source_p->name, name); + return; + } + } +#endif + + /* jdc + * - In the case of a server kicking a user (i.e. CLEARCHAN), + * the kick should show up as coming from the server which did + * the kick. + * - Personally, flame and I believe that server kicks shouldn't + * be sent anyways. Just waiting for some oper to abuse it... + */ + if (IsServer(source_p)) + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s KICK %s %s :%s", + source_p->name, name, who->name, comment); + else + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s KICK %s %s :%s", + source_p->name, source_p->username, + source_p->host, name, who->name, comment); + + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s KICK %s %s :%s", + ID(source_p), chptr->chname, ID(who), comment); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s KICK %s %s :%s", source_p->name, chptr->chname, + who->name, comment); + + remove_user_from_channel(ms_target); + } + else + sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL), + from, to, user, name); +} + +static struct Message kick_msgtab = { + "KICK", 0, 0, 3, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_kick, m_kick, m_ignore, m_kick, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&kick_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&kick_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_kill.c b/modules/core/m_kill.c new file mode 100644 index 0000000..a76e5a4 --- /dev/null +++ b/modules/core/m_kill.c @@ -0,0 +1,331 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_kill.c: Kills a user. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "client.h" +#include "hash.h" /* for find_client() */ +#include "ircd.h" +#include "numeric.h" +#include "log.h" +#include "s_serv.h" +#include "conf.h" +#include "send.h" +#include "whowas.h" +#include "irc_string.h" +#include "sprintf_irc.h" +#include "parse.h" +#include "modules.h" + + +static char buf[IRCD_BUFSIZE]; + +static void +relay_kill(struct Client *one, struct Client *source_p, + struct Client *target_p, const char *inpath, + const char *reason) +{ + dlink_node *ptr = NULL; + + DLINK_FOREACH(ptr, serv_list.head) + { + struct Client *client_p = ptr->data; + + if (client_p == one) + continue; + + if (MyClient(source_p)) + sendto_one(client_p, ":%s KILL %s :%s!%s!%s!%s (%s)", + ID_or_name(source_p, client_p), + ID_or_name(target_p, client_p), + me.name, source_p->host, source_p->username, + source_p->name, reason); + else + sendto_one(client_p, ":%s KILL %s :%s %s", + ID_or_name(source_p, client_p), + ID_or_name(target_p, client_p), inpath, reason); + } +} + +/* mo_kill() + * parv[0] = sender prefix + * parv[1] = kill victim + * parv[2] = kill path + */ +static void +mo_kill(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p; + const char *inpath = client_p->name; + char *user; + char *reason; + char def_reason[] = "No reason"; + + user = parv[1]; + reason = parv[2]; /* Either defined or NULL (parc >= 2!!) */ + + if (*user == '\0') + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "KILL"); + return; + } + + if (!HasOFlag(source_p, OPER_FLAG_GLOBAL_KILL|OPER_FLAG_K)) + { + sendto_one(source_p, form_str(ERR_NOPRIVILEGES), + me.name, source_p->name); + return; + } + + if (!EmptyString(reason)) + { + if (strlen(reason) > (size_t)KILLLEN) + reason[KILLLEN] = '\0'; + } + else + reason = def_reason; + + if ((target_p = hash_find_client(user)) == NULL) + { + /* + * If the user has recently changed nick, automatically + * rewrite the KILL for this new nickname--this keeps + * servers in synch when nick change and kill collide + */ + if ((target_p = get_history(user, + (time_t)ConfigFileEntry.kill_chase_time_limit)) + == NULL) + { + sendto_one(source_p, form_str(ERR_NOSUCHNICK), + me.name, source_p->name, user); + return; + } + + sendto_one(source_p, ":%s NOTICE %s :KILL changed from %s to %s", + me.name, source_p->name, user, target_p->name); + } + + if (IsServer(target_p) || IsMe(target_p)) + { + sendto_one(source_p, form_str(ERR_CANTKILLSERVER), + me.name, source_p->name); + return; + } + + if (!MyConnect(target_p) && !HasOFlag(source_p, OPER_FLAG_GLOBAL_KILL)) + { + sendto_one(source_p, ":%s NOTICE %s :Nick %s isnt on your server", + me.name, source_p->name, target_p->name); + return; + } + + if (MyConnect(target_p)) + sendto_one(target_p, ":%s!%s@%s KILL %s :%s", + source_p->name, source_p->username, source_p->host, + target_p->name, reason); + + /* + * Do not change the format of this message. There's no point in changing messages + * that have been around for ever, for no reason.. + */ + sendto_realops_flags(UMODE_ALL, L_ALL, + "Received KILL message for %s. From %s Path: %s (%s)", + target_p->name, source_p->name, me.name, reason); + + ilog(LOG_TYPE_KILL, "KILL From %s For %s Path %s (%s)", + source_p->name, target_p->name, me.name, reason); + + /* + * And pass on the message to other servers. Note, that if KILL + * was changed, the message has to be sent to all links, also + * back. + * Suicide kills are NOT passed on --SRB + */ + if (!MyConnect(target_p)) + { + relay_kill(client_p, source_p, target_p, inpath, reason); + /* + * Set FLAGS_KILLED. This prevents exit_one_client from sending + * the unnecessary QUIT for this. (This flag should never be + * set in any other place) + */ + AddFlag(target_p, FLAGS_KILLED); + } + + snprintf(buf, sizeof(buf), "Killed (%s (%s))", source_p->name, reason); + exit_client(target_p, source_p, buf); +} + +/* ms_kill() + * parv[0] = sender prefix + * parv[1] = kill victim + * parv[2] = kill path and reason + */ +static void +ms_kill(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p; + char *user; + char *reason; + const char *path; + char def_reason[] = "No reason"; + + if (EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "KILL"); + return; + } + + user = parv[1]; + + if (EmptyString(parv[2])) + { + reason = def_reason; + + /* hyb6 takes the nick of the killer from the path *sigh* --fl_ */ + path = source_p->name; + } + else + { + reason = strchr(parv[2], ' '); + + if (reason != NULL) + *reason++ = '\0'; + else + reason = def_reason; + + path = parv[2]; + } + + if ((target_p = find_person(client_p, user)) == NULL) + { + /* + * If the user has recently changed nick, but only if its + * not an uid, automatically rewrite the KILL for this new nickname. + * --this keeps servers in synch when nick change and kill collide + */ + if (IsDigit(*user)) /* Somehow an uid was not found in the hash ! */ + return; + if ((target_p = get_history(user, + (time_t)ConfigFileEntry.kill_chase_time_limit)) + == NULL) + { + sendto_one(source_p, form_str(ERR_NOSUCHNICK), + me.name, source_p->name, user); + return; + } + + sendto_one(source_p,":%s NOTICE %s :KILL changed from %s to %s", + me.name, source_p->name, user, target_p->name); + } + + if (IsServer(target_p) || IsMe(target_p)) + { + sendto_one(source_p, form_str(ERR_CANTKILLSERVER), + me.name, source_p->name); + return; + } + + if (MyConnect(target_p)) + { + if (IsServer(source_p)) + { + /* dont send clients kills from a hidden server */ + if ((IsHidden(source_p) || ConfigServerHide.hide_servers) && !HasUMode(target_p, UMODE_OPER)) + sendto_one(target_p, ":%s KILL %s :%s", + me.name, target_p->name, reason); + else + sendto_one(target_p, ":%s KILL %s :%s", + source_p->name, target_p->name, reason); + } + else + sendto_one(target_p, ":%s!%s@%s KILL %s :%s", + source_p->name, source_p->username, source_p->host, + target_p->name, reason); + } + + /* + * Be warned, this message must be From %s, or it confuses clients + * so dont change it to From: or the case or anything! -- fl -- db + */ + /* + * path must contain at least 2 !'s, or bitchx falsely declares it + * local --fl + */ + if (HasUMode(source_p, UMODE_OPER)) /* send it normally */ + sendto_realops_flags(UMODE_ALL, L_ALL, + "Received KILL message for %s. From %s Path: %s!%s!%s!%s %s", + target_p->name, source_p->name, source_p->servptr->name, + source_p->host, source_p->username, source_p->name, reason); + else + sendto_realops_flags(UMODE_SKILL, L_ALL, + "Received KILL message for %s. From %s %s", + target_p->name, source_p->name, reason); + + ilog(LOG_TYPE_KILL, "KILL From %s For %s Path %s %s", + source_p->name, target_p->name, source_p->name, reason); + + relay_kill(client_p, source_p, target_p, path, reason); + AddFlag(target_p, FLAGS_KILLED); + + /* reason comes supplied with its own ()'s */ + if (IsServer(source_p) && (IsHidden(source_p) || ConfigServerHide.hide_servers)) + snprintf(buf, sizeof(buf), "Killed (%s %s)", me.name, reason); + else + snprintf(buf, sizeof(buf), "Killed (%s %s)", source_p->name, reason); + + exit_client(target_p, source_p, buf); +} + + +static struct Message kill_msgtab = { + "KILL", 0, 0, 2, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_not_oper, ms_kill, m_ignore, mo_kill, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&kill_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&kill_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_message.c b/modules/core/m_message.c new file mode 100644 index 0000000..183154f --- /dev/null +++ b/modules/core/m_message.c @@ -0,0 +1,935 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_message.c: Sends a (PRIVMSG|NOTICE) message to a user or channel. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "client.h" +#include "ircd.h" +#include "numeric.h" +#include "conf.h" +#include "s_serv.h" +#include "send.h" +#include "parse.h" +#include "modules.h" +#include "channel.h" +#include "channel_mode.h" +#include "irc_string.h" +#include "hash.h" +#include "packet.h" + + +struct entity +{ + void *ptr; + int type; + int flags; +}; + +static int build_target_list(int p_or_n, const char *command, + struct Client *client_p, + struct Client *source_p, + char *nicks_channels, char *text); + +static int flood_attack_client(int p_or_n, struct Client *source_p, + struct Client *target_p); +static int flood_attack_channel(int p_or_n, struct Client *source_p, + struct Channel *chptr); +static struct Client* find_userhost (char *, char *, int *); + +#define ENTITY_NONE 0 +#define ENTITY_CHANNEL 1 +#define ENTITY_CHANOPS_ON_CHANNEL 2 +#define ENTITY_CLIENT 3 + +static struct entity targets[512]; +static int ntargets = 0; + +static int duplicate_ptr(void *); + +static void m_message(int, const char *, struct Client *, + struct Client *, int, char **); + +static void msg_channel(int p_or_n, const char *command, + struct Client *client_p, + struct Client *source_p, + struct Channel *chptr, char *text); + +static void msg_channel_flags(int p_or_n, const char *command, + struct Client *client_p, + struct Client *source_p, + struct Channel *chptr, int flags, char *text); + +static void msg_client(int p_or_n, const char *command, + struct Client *source_p, struct Client *target_p, + char *text); + +static void handle_special(int p_or_n, const char *command, + struct Client *client_p, + struct Client *source_p, char *nick, char *text); + +/* +** m_privmsg +** +** massive cleanup +** rev argv 6/91 +** +** Another massive cleanup Nov, 2000 +** (I don't think there is a single line left from 6/91. Maybe.) +** m_privmsg and m_notice do basically the same thing. +** in the original 2.8.2 code base, they were the same function +** "m_message.c." When we did the great cleanup in conjuncton with bleep +** of ircu fame, we split m_privmsg.c and m_notice.c. +** I don't see the point of that now. Its harder to maintain, its +** easier to introduce bugs into one version and not the other etc. +** Really, the penalty of an extra function call isn't that big a deal folks. +** -db Nov 13, 2000 +** +*/ + +#define PRIVMSG 0 +#define NOTICE 1 + +static void +m_privmsg(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + /* servers have no reason to send privmsgs, yet sometimes there is cause + * for a notice.. (for example remote kline replies) --fl_ + */ + if (!IsClient(source_p)) + return; + + m_message(PRIVMSG, "PRIVMSG", client_p, source_p, parc, parv); +} + +static void +m_notice(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + m_message(NOTICE, "NOTICE", client_p, source_p, parc, parv); +} + +/* + * inputs - flag privmsg or notice + * - pointer to command "PRIVMSG" or "NOTICE" + * - pointer to client_p + * - pointer to source_p + * - pointer to channel + */ +static void +m_message(int p_or_n, const char *command, struct Client *client_p, + struct Client *source_p, int parc, char *parv[]) +{ + int i; + + if (parc < 2 || EmptyString(parv[1])) + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_NORECIPIENT), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), command); + return; + } + + if (parc < 3 || EmptyString(parv[2])) + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p)); + return; + } + + /* Finish the flood grace period... */ + if (MyClient(source_p) && !IsFloodDone(source_p)) + flood_endgrace(source_p); + + if (build_target_list(p_or_n, command, client_p, source_p, parv[1], + parv[2]) < 0) + return; + + for (i = 0; i < ntargets; i++) + { + switch (targets[i].type) + { + case ENTITY_CHANNEL: + msg_channel(p_or_n, command, client_p, source_p, + (struct Channel *)targets[i].ptr, parv[2]); + break; + + case ENTITY_CHANOPS_ON_CHANNEL: + msg_channel_flags(p_or_n, command, client_p, source_p, + (struct Channel *)targets[i].ptr, + targets[i].flags, parv[2]); + break; + + case ENTITY_CLIENT: + msg_client(p_or_n, command, source_p, + (struct Client *)targets[i].ptr, parv[2]); + break; + } + } +} + +/* build_target_list() + * + * inputs - pointer to given client_p (server) + * - pointer to given source (oper/client etc.) + * - pointer to list of nicks/channels + * - pointer to table to place results + * - pointer to text (only used if source_p is an oper) + * output - number of valid entities + * side effects - target_table is modified to contain a list of + * pointers to channels or clients + * if source client is an oper + * all the classic old bizzare oper privmsg tricks + * are parsed and sent as is, if prefixed with $ + * to disambiguate. + * + */ +static int +build_target_list(int p_or_n, const char *command, struct Client *client_p, + struct Client *source_p, char *nicks_channels, char *text) +{ + int type; + char *p = NULL, *nick, *target_list; + struct Channel *chptr = NULL; + struct Client *target_p = NULL; + + target_list = nicks_channels; + + ntargets = 0; + + for (nick = strtoken(&p, target_list, ","); nick; + nick = strtoken(&p, NULL, ",")) + { + char *with_prefix; + /* + * channels are privmsg'd a lot more than other clients, moved up + * here plain old channel msg? + */ + + if (IsChanPrefix(*nick)) + { + if ((chptr = hash_find_channel(nick)) != NULL) + { + if (!duplicate_ptr(chptr)) + { + if (ntargets >= ConfigFileEntry.max_targets) + { + sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick, + ConfigFileEntry.max_targets); + return (1); + } + targets[ntargets].ptr = (void *)chptr; + targets[ntargets++].type = ENTITY_CHANNEL; + } + } + else + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_NOSUCHNICK), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick); + } + continue; + } + + /* look for a privmsg to another client */ + if ((target_p = find_person(client_p, nick)) != NULL) + { + if (!duplicate_ptr(target_p)) + { + if (ntargets >= ConfigFileEntry.max_targets) + { + sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick, + ConfigFileEntry.max_targets); + return (1); + } + targets[ntargets].ptr = (void *)target_p; + targets[ntargets].type = ENTITY_CLIENT; + targets[ntargets++].flags = 0; + } + continue; + } + + /* @#channel or +#channel message ? */ + + type = 0; + with_prefix = nick; + /* allow %+@ if someone wants to do that */ + for (; ;) + { + if (*nick == '@') + type |= CHFL_CHANOP; +#ifdef HALFOPS + else if (*nick == '%') + type |= CHFL_CHANOP | CHFL_HALFOP; +#endif + else if (*nick == '+') + type |= CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE; + else + break; + nick++; + } + + if (type != 0) + { + /* suggested by Mortiis */ + if (*nick == '\0') /* if its a '\0' dump it, there is no recipient */ + { + sendto_one(source_p, form_str(ERR_NORECIPIENT), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), command); + continue; + } + + /* At this point, nick+1 should be a channel name i.e. #foo or &foo + * if the channel is found, fine, if not report an error + */ + + if ((chptr = hash_find_channel(nick)) != NULL) + { + if (IsClient(source_p) && !HasFlag(source_p, FLAGS_SERVICE)) + { + if (!has_member_flags(find_channel_link(source_p, chptr), + CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE)) + { + sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), with_prefix); + return(-1); + } + } + + if (!duplicate_ptr(chptr)) + { + if (ntargets >= ConfigFileEntry.max_targets) + { + sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick, + ConfigFileEntry.max_targets); + return(1); + } + targets[ntargets].ptr = (void *)chptr; + targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL; + targets[ntargets++].flags = type; + } + } + else + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_NOSUCHNICK), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick); + } + continue; + } + + if ((*nick == '$') || strchr(nick, '@') != NULL) + { + handle_special(p_or_n, command, client_p, source_p, nick, text); + } + else + { + if (p_or_n != NOTICE) + { + if (!IsDigit(*nick) || MyClient(source_p)) + sendto_one(source_p, form_str(ERR_NOSUCHNICK), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick); + } + } + /* continue; */ + } + + return(1); +} + +/* duplicate_ptr() + * + * inputs - pointer to check + * - pointer to table of entities + * - number of valid entities so far + * output - YES if duplicate pointer in table, NO if not. + * note, this does the canonize using pointers + * side effects - NONE + */ +static int +duplicate_ptr(void *ptr) +{ + int i; + + for (i = 0; i < ntargets; i++) + { + if (targets[i].ptr == ptr) + return(1); + } + + return(0); +} + +/* msg_channel() + * + * inputs - flag privmsg or notice + * - pointer to command "PRIVMSG" or "NOTICE" + * - pointer to client_p + * - pointer to source_p + * - pointer to channel + * output - NONE + * side effects - message given channel + */ +static void +msg_channel(int p_or_n, const char *command, struct Client *client_p, + struct Client *source_p, struct Channel *chptr, char *text) +{ + int result; + + if (MyClient(source_p)) + { + /* idle time shouldnt be reset by notices --fl */ + if (p_or_n != NOTICE) + source_p->localClient->last_privmsg = CurrentTime; + } + + /* chanops and voiced can flood their own channel with impunity */ + if ((result = can_send(chptr, source_p, NULL)) < 0) + { + if (result == CAN_SEND_OPV || + !flood_attack_channel(p_or_n, source_p, chptr)) + sendto_channel_butone(client_p, source_p, chptr, 0, "%s %s :%s", + command, chptr->chname, text); + } + else + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_CANNOTSENDTOCHAN), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), chptr->chname); + } +} + +/* msg_channel_flags() + * + * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC + * say NOTICE must not auto reply + * - pointer to command, "PRIVMSG" or "NOTICE" + * - pointer to client_p + * - pointer to source_p + * - pointer to channel + * - flags + * - pointer to text to send + * output - NONE + * side effects - message given channel either chanop or voice + */ +static void +msg_channel_flags(int p_or_n, const char *command, struct Client *client_p, + struct Client *source_p, struct Channel *chptr, + int flags, char *text) +{ + unsigned int type; + char c; + + if (flags & CHFL_VOICE) + { + type = CHFL_VOICE|CHFL_HALFOP|CHFL_CHANOP; + c = '+'; + } +#ifdef HALFOPS + else if (flags & CHFL_HALFOP) + { + type = CHFL_HALFOP|CHFL_CHANOP; + c = '%'; + } +#endif + else + { + type = CHFL_CHANOP; + c = '@'; + } + + if (MyClient(source_p) && p_or_n != NOTICE) + source_p->localClient->last_privmsg = CurrentTime; + + sendto_channel_butone(client_p, source_p, chptr, type, "%s %c%s :%s", + command, c, chptr->chname, text); +} + +/* msg_client() + * + * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC + * say NOTICE must not auto reply + * - pointer to command, "PRIVMSG" or "NOTICE" + * - pointer to source_p source (struct Client *) + * - pointer to target_p target (struct Client *) + * - pointer to text + * output - NONE + * side effects - message given channel either chanop or voice + */ +static void +msg_client(int p_or_n, const char *command, struct Client *source_p, + struct Client *target_p, char *text) +{ + if (MyConnect(source_p)) + { + /* + * reset idle time for message only if it's not a notice + */ + if ((p_or_n != NOTICE)) + source_p->localClient->last_privmsg = CurrentTime; + + if ((p_or_n != NOTICE) && target_p->away[0]) + sendto_one(source_p, form_str(RPL_AWAY), me.name, + source_p->name, target_p->name, target_p->away); + + if (HasUMode(target_p, UMODE_REGONLY) && target_p != source_p) + { + if (!HasUMode(source_p, UMODE_REGISTERED|UMODE_OPER)) + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_NONONREG), me.name, source_p->name, + target_p->name); + return; + } + } + } + + if (MyClient(target_p)) + { + if (!IsServer(source_p) && HasUMode(target_p, UMODE_CALLERID|UMODE_SOFTCALLERID)) + { + /* Here is the anti-flood bot/spambot code -db */ + if (accept_message(source_p, target_p) || HasFlag(source_p, FLAGS_SERVICE) || + (HasUMode(source_p, UMODE_OPER) && (ConfigFileEntry.opers_bypass_callerid == 1))) + { + sendto_one(target_p, ":%s!%s@%s %s %s :%s", + source_p->name, source_p->username, + source_p->host, command, target_p->name, text); + } + else + { + /* check for accept, flag recipient incoming message */ + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(RPL_TARGUMODEG), + ID_or_name(&me, source_p->from), + ID_or_name(source_p, source_p->from), target_p->name); + + if ((target_p->localClient->last_caller_id_time + + ConfigFileEntry.caller_id_wait) < CurrentTime) + { + if (p_or_n != NOTICE) + sendto_one(source_p, form_str(RPL_TARGNOTIFY), + ID_or_name(&me, source_p->from), + ID_or_name(source_p, source_p->from), target_p->name); + + sendto_one(target_p, form_str(RPL_UMODEGMSG), + me.name, target_p->name, + get_client_name(source_p, HIDE_IP)); + + target_p->localClient->last_caller_id_time = CurrentTime; + + } + /* Only so opers can watch for floods */ + flood_attack_client(p_or_n, source_p, target_p); + } + } + else + { + /* If the client is remote, we dont perform a special check for + * flooding.. as we wouldnt block their message anyway.. this means + * we dont give warnings.. we then check if theyre opered + * (to avoid flood warnings), lastly if theyre our client + * and flooding -- fl */ + if (!MyClient(source_p) || HasUMode(source_p, UMODE_OPER) || + (MyClient(source_p) && + !flood_attack_client(p_or_n, source_p, target_p))) + sendto_anywhere(target_p, source_p, "%s %s :%s", + command, target_p->name, text); + } + } + else + /* The target is a remote user.. same things apply -- fl */ + if (!MyClient(source_p) || HasUMode(source_p, UMODE_OPER) || + (MyClient(source_p) + && !flood_attack_client(p_or_n, source_p, target_p))) + sendto_anywhere(target_p, source_p, "%s %s :%s", command, target_p->name, + text); +} + +/* flood_attack_client() + * + * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC + * say NOTICE must not auto reply + * - pointer to source Client + * - pointer to target Client + * output - 1 if target is under flood attack + * side effects - check for flood attack on target target_p + */ +static int +flood_attack_client(int p_or_n, struct Client *source_p, + struct Client *target_p) +{ + int delta; + + if (GlobalSetOptions.floodcount && MyConnect(target_p) + && IsClient(source_p) && !IsConfCanFlood(source_p)) + { + if ((target_p->localClient->first_received_message_time + 1) + < CurrentTime) + { + delta = + CurrentTime - target_p->localClient->first_received_message_time; + target_p->localClient->received_number_of_privmsgs -= delta; + target_p->localClient->first_received_message_time = CurrentTime; + + if (target_p->localClient->received_number_of_privmsgs <= 0) + { + target_p->localClient->received_number_of_privmsgs = 0; + DelFlag(target_p, FLAGS_FLOOD_NOTICED); + } + } + + if ((target_p->localClient->received_number_of_privmsgs >= + GlobalSetOptions.floodcount) || HasFlag(target_p, FLAGS_FLOOD_NOTICED)) + { + if (!HasFlag(target_p, FLAGS_FLOOD_NOTICED)) + { + sendto_realops_flags(UMODE_BOTS, L_ALL, + "Possible Flooder %s on %s target: %s", + get_client_name(source_p, HIDE_IP), + source_p->servptr->name, target_p->name); + AddFlag(target_p, FLAGS_FLOOD_NOTICED); + /* add a bit of penalty */ + target_p->localClient->received_number_of_privmsgs += 2; + } + + if (MyClient(source_p) && (p_or_n != NOTICE)) + sendto_one(source_p, + ":%s NOTICE %s :*** Message to %s throttled due to flooding", + me.name, source_p->name, target_p->name); + return(1); + } + else + target_p->localClient->received_number_of_privmsgs++; + } + + return(0); +} + +/* flood_attack_channel() + * + * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC + * says NOTICE must not auto reply + * - pointer to source Client + * - pointer to target channel + * output - 1 if target is under flood attack + * side effects - check for flood attack on target chptr + */ +static int +flood_attack_channel(int p_or_n, struct Client *source_p, + struct Channel *chptr) +{ + int delta; + + if (GlobalSetOptions.floodcount && !IsConfCanFlood(source_p)) + { + if ((chptr->first_received_message_time + 1) < CurrentTime) + { + delta = CurrentTime - chptr->first_received_message_time; + chptr->received_number_of_privmsgs -= delta; + chptr->first_received_message_time = CurrentTime; + if (chptr->received_number_of_privmsgs <= 0) + { + chptr->received_number_of_privmsgs = 0; + ClearFloodNoticed(chptr); + } + } + + if ((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount) + || IsSetFloodNoticed(chptr)) + { + if (!IsSetFloodNoticed(chptr)) + { + sendto_realops_flags(UMODE_BOTS, L_ALL, + "Possible Flooder %s on %s target: %s", + get_client_name(source_p, HIDE_IP), + source_p->servptr->name, chptr->chname); + SetFloodNoticed(chptr); + + /* Add a bit of penalty */ + chptr->received_number_of_privmsgs += 2; + } + if (MyClient(source_p) && (p_or_n != NOTICE)) + sendto_one(source_p, + ":%s NOTICE %s :*** Message to %s throttled due to flooding", + me.name, source_p->name, chptr->chname); + return(1); + } + else + chptr->received_number_of_privmsgs++; + } + + return(0); +} + +/* handle_special() + * + * inputs - server pointer + * - client pointer + * - nick stuff to grok for opers + * - text to send if grok + * output - none + * side effects - old style username@server is handled here for non opers + * opers are allowed username%hostname@server + * all the traditional oper type messages are also parsed here. + * i.e. "/msg #some.host." + * However, syntax has been changed. + * previous syntax "/msg #some.host.mask" + * now becomes "/msg $#some.host.mask" + * previous syntax of: "/msg $some.server.mask" remains + * This disambiguates the syntax. + * + * XXX N.B. dalnet changed it to nick@server as have other servers. + * we will stick with tradition for now. + * - Dianora + */ +static void +handle_special(int p_or_n, const char *command, struct Client *client_p, + struct Client *source_p, char *nick, char *text) +{ + struct Client *target_p; + char *host; + char *server; + char *s; + int count; + + /* + * user[%host]@server addressed? + */ + if ((server = strchr(nick, '@')) != NULL) + { + count = 0; + + if ((host = strchr(nick, '%')) && !HasUMode(source_p, UMODE_OPER)) + { + sendto_one(source_p, form_str(ERR_NOPRIVILEGES), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p)); + return; + } + + if ((target_p = hash_find_server(server + 1)) != NULL) + { + if (!IsMe(target_p)) + { + /* + * Not destined for a user on me :-( + */ + sendto_one(target_p, ":%s %s %s :%s", + ID_or_name(source_p, target_p->from), + command, nick, text); + if ((p_or_n != NOTICE) && MyClient(source_p)) + source_p->localClient->last_privmsg = CurrentTime; + return; + } + + *server = '\0'; + + if (host != NULL) + *host++ = '\0'; + + /* + * Look for users which match the destination host + * (no host == wildcard) and if one and one only is + * found connected to me, deliver message! + */ + target_p = find_userhost(nick, host, &count); + + if (target_p != NULL) + { + if (server != NULL) + *server = '@'; + if (host != NULL) + *--host = '%'; + + if (count == 1) + { + sendto_one(target_p, ":%s!%s@%s %s %s :%s", + source_p->name, source_p->username, source_p->host, + command, nick, text); + if ((p_or_n != NOTICE) && MyClient(source_p)) + source_p->localClient->last_privmsg = CurrentTime; + } + else + sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick, + ConfigFileEntry.max_targets); + } + } + else if (server && *(server+1) && (target_p == NULL)) + sendto_one(source_p, form_str(ERR_NOSUCHSERVER), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), server+1); + else if (server && (target_p == NULL)) + sendto_one(source_p, form_str(ERR_NOSUCHNICK), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick); + return; + } + + if (!HasUMode(source_p, UMODE_OPER)) + { + sendto_one(source_p, form_str(ERR_NOPRIVILEGES), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p)); + return; + } + + /* + * the following two cases allow masks in NOTICEs + * (for OPERs only) + * + * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de) + */ + if (*nick == '$') + { + if ((*(nick+1) == '$' || *(nick+1) == '#')) + nick++; + else if (MyClient(source_p) && HasUMode(source_p, UMODE_OPER)) + { + sendto_one(source_p, + ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s", + me.name, source_p->name, command, nick, nick); + return; + } + + if ((s = strrchr(nick, '.')) == NULL) + { + sendto_one(source_p, form_str(ERR_NOTOPLEVEL), + me.name, source_p->name, nick); + return; + } + + while (*++s) + if (*s == '.' || *s == '*' || *s == '?') + break; + + if (*s == '*' || *s == '?') + { + sendto_one(source_p, form_str(ERR_WILDTOPLEVEL), + ID_or_name(&me, client_p), + ID_or_name(source_p, client_p), nick); + return; + } + + sendto_match_butone(IsServer(client_p) ? client_p : NULL, source_p, + nick + 1, (*nick == '#') ? MATCH_HOST : MATCH_SERVER, + "%s $%s :%s", command, nick, text); + + if ((p_or_n != NOTICE) && MyClient(source_p)) + source_p->localClient->last_privmsg = CurrentTime; + + return; + } +} + +/* + * find_userhost - find a user@host (server or user). + * inputs - user name to look for + * - host name to look for + * - pointer to count of number of matches found + * outputs - pointer to client if found + * - count is updated + * side effects - none + * + */ +static struct Client * +find_userhost(char *user, char *host, int *count) +{ + struct Client *c2ptr; + struct Client *res = NULL; + dlink_node *lc2ptr; + + *count = 0; + + if (collapse(user) != NULL) + { + DLINK_FOREACH(lc2ptr, local_client_list.head) + { + c2ptr = lc2ptr->data; + + if (!IsClient(c2ptr)) /* something other than a client */ + continue; + + if ((!host || match(host, c2ptr->host)) && + irccmp(user, c2ptr->username) == 0) + { + (*count)++; + res = c2ptr; + } + } + } + + return(res); +} + +static struct Message privmsg_msgtab = { + "PRIVMSG", 0, 0, 0, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_privmsg, m_privmsg, m_ignore, m_privmsg, m_ignore} +}; + +static struct Message notice_msgtab = { + "NOTICE", 0, 0, 0, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_notice, m_notice, m_ignore, m_notice, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&privmsg_msgtab); + mod_add_cmd(¬ice_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&privmsg_msgtab); + mod_del_cmd(¬ice_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_mode.c b/modules/core/m_mode.c new file mode 100644 index 0000000..fa68b22 --- /dev/null +++ b/modules/core/m_mode.c @@ -0,0 +1,313 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_mode.c: Sets a user or channel mode. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "hash.h" +#include "irc_string.h" +#include "sprintf_irc.h" +#include "ircd.h" +#include "numeric.h" +#include "s_user.h" +#include "conf.h" +#include "s_serv.h" +#include "send.h" +#include "parse.h" +#include "modules.h" +#include "packet.h" + + +/* + * m_mode - MODE command handler + * parv[0] - sender + * parv[1] - channel + */ +static void +m_mode(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Channel *chptr = NULL; + struct Membership *member; + static char modebuf[MODEBUFLEN]; + static char parabuf[MODEBUFLEN]; + + if (EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "MODE"); + return; + } + + /* Now, try to find the channel in question */ + if (!IsChanPrefix(*parv[1])) + { + /* if here, it has to be a non-channel name */ + set_user_mode(client_p, source_p, parc, parv); + return; + } + + if ((chptr = hash_find_channel(parv[1])) == NULL) + { + sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), + ID_or_name(&me, source_p->from), + ID_or_name(source_p, source_p->from), + parv[1]); + return; + } + + /* Now known the channel exists */ + if (parc < 3) + { + channel_modes(chptr, source_p, modebuf, parabuf); + sendto_one(source_p, form_str(RPL_CHANNELMODEIS), + me.name, source_p->name, chptr->chname, modebuf, parabuf); + sendto_one(source_p, form_str(RPL_CREATIONTIME), + me.name, source_p->name, chptr->chname, chptr->channelts); + } + /* bounce all modes from people we deop on sjoin + * servers have always gotten away with murder, + * including telnet servers *g* - Dianora + * + * XXX Is it worth the bother to make an ms_mode() ? - Dianora + */ + else if (IsServer(source_p)) + { + set_channel_mode(client_p, source_p, chptr, NULL, parc - 2, parv + 2, + chptr->chname); + } + else + { + member = find_channel_link(source_p, chptr); + + if (!has_member_flags(member, CHFL_DEOPPED)) + { + /* Finish the flood grace period... */ + if (MyClient(source_p) && !IsFloodDone(source_p)) + { + if (!((parc == 3) && (parv[2][0] == 'b') && (parv[2][1] == '\0'))) + flood_endgrace(source_p); + } + + set_channel_mode(client_p, source_p, chptr, member, parc - 2, parv + 2, + chptr->chname); + } + } +} + +/* + * ms_tmode() + * + * inputs - parv[0] = UID + * parv[1] = TS + * parv[2] = channel name + * parv[3] = modestring + */ +static void +ms_tmode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) +{ + struct Channel *chptr = NULL; + struct Membership *member = NULL; + + if ((chptr = hash_find_channel(parv[2])) == NULL) + { + sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), + ID_or_name(&me, client_p), ID_or_name(source_p, client_p), parv[2]); + return; + } + + if (atol(parv[1]) > chptr->channelts) + return; + + if (IsServer(source_p)) + set_channel_mode(client_p, source_p, chptr, NULL, parc - 3, parv + 3, chptr->chname); + else + { + member = find_channel_link(source_p, chptr); + + /* XXX are we sure we just want to bail here? */ + if (has_member_flags(member, CHFL_DEOPPED)) + return; + + set_channel_mode(client_p, source_p, chptr, member, parc - 3, parv + 3, chptr->chname); + } +} + +/* + * ms_bmask() + * + * inputs - parv[0] = SID + * parv[1] = TS + * parv[2] = channel name + * parv[3] = type of ban to add ('b' 'I' or 'e') + * parv[4] = space delimited list of masks to add + * outputs - none + * side effects - propagates unchanged bmask line to CAP_TS6 servers, + * sends plain modes to the others. nothing is sent + * to the server the issuing server is connected through + */ +static void +ms_bmask(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) +{ + static char modebuf[IRCD_BUFSIZE]; + static char parabuf[IRCD_BUFSIZE]; + static char banbuf[IRCD_BUFSIZE]; + struct Channel *chptr; + char *s, *t, *mbuf, *pbuf; + long mode_type; + int mlen, tlen; + int modecount = 0; + int needcap = NOCAPS; + + if ((chptr = hash_find_channel(parv[2])) == NULL) + return; + + /* TS is higher, drop it. */ + if (atol(parv[1]) > chptr->channelts) + return; + + switch (*parv[3]) + { + case 'b': + mode_type = CHFL_BAN; + break; + + case 'e': + mode_type = CHFL_EXCEPTION; + needcap = CAP_EX; + break; + + case 'I': + mode_type = CHFL_INVEX; + needcap = CAP_IE; + break; + + /* maybe we should just blindly propagate this? */ + default: + return; + } + + parabuf[0] = '\0'; + s = banbuf; + strlcpy(s, parv[4], sizeof(banbuf)); + + /* only need to construct one buffer, for non-ts6 servers */ + mlen = ircsprintf(modebuf, ":%s MODE %s +", + source_p->name, chptr->chname); + mbuf = modebuf + mlen; + pbuf = parabuf; + + do { + if ((t = strchr(s, ' ')) != NULL) + *t++ = '\0'; + tlen = strlen(s); + + /* I dont even want to begin parsing this.. */ + if (tlen > MODEBUFLEN) + break; + + if (tlen && *s != ':' && add_id(source_p, chptr, s, mode_type)) + { + /* this new one wont fit.. */ + if (mbuf - modebuf + 2 + pbuf - parabuf + tlen > IRCD_BUFSIZE - 2 || + modecount >= MAXMODEPARAMS) + { + *mbuf = '\0'; + *(pbuf - 1) = '\0'; + + sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", + modebuf, parabuf); + sendto_server(client_p, needcap, CAP_TS6, + "%s %s", modebuf, parabuf); + + mbuf = modebuf + mlen; + pbuf = parabuf; + modecount = 0; + } + + *mbuf++ = parv[3][0]; + pbuf += ircsprintf(pbuf, "%s ", s); + modecount++; + } + + s = t; + } while (s != NULL); + + if (modecount) + { + *mbuf = *(pbuf - 1) = '\0'; + sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", modebuf, parabuf); + sendto_server(client_p, needcap, CAP_TS6, + "%s %s", modebuf, parabuf); + } + + /* assumption here is that since the server sent BMASK, they are TS6, so they have an ID */ + sendto_server(client_p, CAP_TS6|needcap, NOCAPS, + ":%s BMASK %lu %s %s :%s", + source_p->id, (unsigned long)chptr->channelts, chptr->chname, + parv[3], parv[4]); +} + +static struct Message mode_msgtab = { + "MODE", 0, 0, 2, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_mode, m_mode, m_ignore, m_mode, m_ignore} +}; + +static struct Message tmode_msgtab = { + "TMODE", 0, 0, 4, MAXPARA, MFLG_SLOW, 0, + {m_ignore, m_ignore, ms_tmode, m_ignore, m_ignore, m_ignore} +}; + +static struct Message bmask_msgtab = { + "BMASK", 0, 0, 5, MAXPARA, MFLG_SLOW, 0, + {m_ignore, m_ignore, ms_bmask, m_ignore, m_ignore, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&mode_msgtab); + mod_add_cmd(&tmode_msgtab); + mod_add_cmd(&bmask_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&mode_msgtab); + mod_del_cmd(&tmode_msgtab); + mod_del_cmd(&bmask_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_nick.c b/modules/core/m_nick.c new file mode 100644 index 0000000..68f4e57 --- /dev/null +++ b/modules/core/m_nick.c @@ -0,0 +1,1004 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_nick.c: Sets a users nick. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "client.h" +#include "hash.h" +#include "fdlist.h" +#include "irc_string.h" +#include "ircd.h" +#include "numeric.h" +#include "conf.h" +#include "s_user.h" +#include "whowas.h" +#include "s_serv.h" +#include "send.h" +#include "channel.h" +#include "channel_mode.h" +#include "log.h" +#include "resv.h" +#include "parse.h" +#include "modules.h" +#include "packet.h" +#include "watch.h" + + +static void nick_from_server(struct Client *, struct Client *, int, char **, + time_t, const char *, char *, char *); +static void uid_from_server(struct Client *, struct Client *, int, char **, + time_t, const char *, char *, char *); +static int check_clean_nick(struct Client *client_p, struct Client *source_p, + char *nick, struct Client *server_p); +static int check_clean_user(struct Client *client_p, char *nick, char *user, + struct Client *server_p); +static int check_clean_host(struct Client *client_p, char *nick, char *host, + struct Client *server_p); + +static int clean_user_name(const char *); +static int clean_host_name(const char *); +static void perform_nick_collides(struct Client *, struct Client *, struct Client *, + int, char **, time_t, const char *, char *, char *, char *); + + +/* set_initial_nick() + * + * inputs + * output + * side effects - + * + * This function is only called to set up an initially registering + * client. + */ +static void +set_initial_nick(struct Client *source_p, const char *nick) +{ + /* Client setting NICK the first time */ + + /* This had to be copied here to avoid problems.. */ + source_p->tsinfo = CurrentTime; + source_p->localClient->registration &= ~REG_NEED_NICK; + + if (source_p->name[0]) + hash_del_client(source_p); + + strlcpy(source_p->name, nick, sizeof(source_p->name)); + hash_add_client(source_p); + + /* fd_desc is long enough */ + fd_note(&source_p->localClient->fd, "Nick: %s", nick); + + if (!source_p->localClient->registration) + register_local_user(source_p); +} + +/* change_local_nick() + * + * inputs - pointer to server + * - pointer to client + * - nick + * output - + * side effects - changes nick of a LOCAL user + */ +static void +change_local_nick(struct Client *source_p, const char *nick) +{ + assert(source_p->name[0] && !EmptyString(nick)); + assert(MyConnect(source_p)); + + /* + * Client just changing his/her nick. If he/she is + * on a channel, send note of change to all clients + * on that channel. Propagate notice to other servers. + */ + if ((source_p->localClient->last_nick_change + + ConfigFileEntry.max_nick_time) < CurrentTime) + source_p->localClient->number_of_nick_changes = 0; + source_p->localClient->last_nick_change = CurrentTime; + source_p->localClient->number_of_nick_changes++; + + if ((ConfigFileEntry.anti_nick_flood && + (source_p->localClient->number_of_nick_changes + <= ConfigFileEntry.max_nick_changes)) || + !ConfigFileEntry.anti_nick_flood || + (HasUMode(source_p, UMODE_OPER) && ConfigFileEntry.no_oper_flood)) + { + int samenick = !irccmp(source_p->name, nick); + + if (!samenick) + { + source_p->tsinfo = CurrentTime; + clear_ban_cache_client(source_p); + watch_check_hash(source_p, RPL_LOGOFF); + + if (HasUMode(source_p, UMODE_REGISTERED)) + { + unsigned int oldmodes = source_p->umodes; + char modebuf[IRCD_BUFSIZE] = { '\0' }; + + DelUMode(source_p, UMODE_REGISTERED); + send_umode(source_p, source_p, oldmodes, 0xffffffff, modebuf); + } + } + + /* XXX - the format of this notice should eventually be changed + * to either %s[%s@%s], or even better would be get_client_name() -bill + */ + sendto_realops_flags(UMODE_NCHANGE, L_ALL, "Nick change: From %s to %s [%s@%s]", + source_p->name, nick, source_p->username, source_p->host); + sendto_common_channels_local(source_p, 1, ":%s!%s@%s NICK :%s", + source_p->name, source_p->username, + source_p->host, nick); + add_history(source_p, 1); + + sendto_server(source_p, CAP_TS6, NOCAPS, + ":%s NICK %s :%lu", + ID(source_p), nick, (unsigned long)source_p->tsinfo); + sendto_server(source_p, NOCAPS, CAP_TS6, + ":%s NICK %s :%lu", + source_p->name, nick, (unsigned long)source_p->tsinfo); + + hash_del_client(source_p); + strcpy(source_p->name, nick); + hash_add_client(source_p); + + if (!samenick) + watch_check_hash(source_p, RPL_LOGON); + + /* fd_desc is long enough */ + fd_note(&source_p->localClient->fd, "Nick: %s", nick); + } + else + sendto_one(source_p, form_str(ERR_NICKTOOFAST), + me.name, source_p->name, source_p->name, + nick, ConfigFileEntry.max_nick_time); +} + +/*! \brief NICK command handler (called by unregistered, + * locally connected clients) + * + * \param client_p Pointer to allocated Client struct with physical connection + * to this server, i.e. with an open socket connected. + * \param source_p Pointer to allocated Client struct from which the message + * originally comes from. This can be a local or remote client. + * \param parc Integer holding the number of supplied arguments. + * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL + * pointers. + * \note Valid arguments for this command are: + * - parv[0] = sender prefix + * - parv[1] = nickname + */ +static void +mr_nick(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p = NULL; + char nick[NICKLEN + 1]; + char *s = NULL; + + if (parc < 2 || EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN), me.name, + source_p->name[0] ? source_p->name : "*"); + return; + } + + /* Terminate the nick at the first ~ */ + if ((s = strchr(parv[1], '~')) != NULL) + *s = '\0'; + + /* copy the nick and terminate it */ + strlcpy(nick, parv[1], sizeof(nick)); + + /* check the nickname is ok */ + if (!valid_nickname(nick, 1)) + { + sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, + source_p->name[0] ? source_p->name : "*", parv[1]); + return; + } + + /* check if the nick is resv'd */ + if (find_matching_name_conf(NRESV_TYPE, nick, NULL, NULL, 0) && + !IsExemptResv(source_p)) + { + sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), me.name, + source_p->name[0] ? source_p->name : "*", nick); + sendto_realops_flags(L_ALL, UMODE_REJ, + "Forbidding reserved nick [%s] from user %s", + nick, get_client_name(client_p, HIDE_IP)); + return; + } + + if ((target_p = hash_find_client(nick)) == NULL) + set_initial_nick(source_p, nick); + else if (source_p == target_p) + strlcpy(source_p->name, nick, sizeof(source_p->name)); + else + sendto_one(source_p, form_str(ERR_NICKNAMEINUSE), me.name, "*", nick); +} + + +/*! \brief NICK command handler (called by already registered, + * locally connected clients) + * + * \param client_p Pointer to allocated Client struct with physical connection + * to this server, i.e. with an open socket connected. + * \param source_p Pointer to allocated Client struct from which the message + * originally comes from. This can be a local or remote client. + * \param parc Integer holding the number of supplied arguments. + * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL + * pointers. + * \note Valid arguments for this command are: + * - parv[0] = sender prefix + * - parv[1] = nickname + */ +static void +m_nick(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char nick[NICKLEN + 1]; + struct Client *target_p = NULL; + + assert(source_p == client_p); + + if (parc < 2 || EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN), + me.name, source_p->name); + return; + } + + /* mark end of grace period, to prevent nickflooding */ + if (!IsFloodDone(source_p)) + flood_endgrace(source_p); + + /* terminate nick to NICKLEN */ + strlcpy(nick, parv[1], sizeof(nick)); + + /* check the nickname is ok */ + if (!valid_nickname(nick, 1)) + { + sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), + me.name, source_p->name, nick); + return; + } + + if (find_matching_name_conf(NRESV_TYPE, nick, + NULL, NULL, 0) && !IsExemptResv(source_p) && + !(HasUMode(source_p, UMODE_OPER) && ConfigFileEntry.oper_pass_resv)) + { + sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), + me.name, source_p->name, nick); + sendto_realops_flags(L_ALL, UMODE_REJ, + "Forbidding reserved nick [%s] from user %s", + nick, get_client_name(client_p, HIDE_IP)); + return; + } + + if ((target_p = hash_find_client(nick)) == NULL) + change_local_nick(source_p, nick); + else if (target_p == source_p) + { + /* + * If (target_p == source_p) the client is changing nicks between + * equivalent nicknames ie: [nick] -> {nick} + */ + + /* check the nick isnt exactly the same */ + if (strcmp(target_p->name, nick)) + change_local_nick(source_p, nick); + } + else if (IsUnknown(target_p)) + { + /* + * if the client that has the nick isn't registered yet (nick but no + * user) then drop the unregged client + */ + exit_client(target_p, &me, "Overridden"); + change_local_nick(source_p, nick); + } + else + sendto_one(source_p, form_str(ERR_NICKNAMEINUSE), me.name, + source_p->name, nick); +} + + +/*! \brief NICK command handler (called by servers and remotely + * connected clients) + * + * \param client_p Pointer to allocated Client struct with physical connection + * to this server, i.e. with an open socket connected. + * \param source_p Pointer to allocated Client struct from which the message + * originally comes from. This can be a local or remote client. + * \param parc Integer holding the number of supplied arguments. + * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL + * pointers. + * \note Valid arguments for this command are: + * + * server -> server nick change + * - parv[0] = sender prefix + * - parv[1] = nickname + * - parv[2] = TS when nick change + * + * server introducing new nick (without services support) + * - parv[0] = sender prefix + * - parv[1] = nickname + * - parv[2] = hop count + * - parv[3] = TS + * - parv[4] = umode + * - parv[5] = username + * - parv[6] = hostname + * - parv[7] = server + * - parv[8] = ircname + * + * server introducing new nick (with services support) + * - parv[0] = sender prefix + * - parv[1] = nickname + * - parv[2] = hop count + * - parv[3] = TS + * - parv[4] = umode + * - parv[5] = username + * - parv[6] = hostname + * - parv[7] = server + * - parv[8] = services id (timestamp) + * - parv[9] = ircname + */ +static void +ms_nick(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p = NULL; + time_t newts = 0; + const char *svsid = "0"; + + if (parc < 3 || EmptyString(parv[parc - 1])) + return; + + if (parc >= 9) + { + struct Client *server_p = hash_find_server(parv[7]); + + if (server_p == NULL) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "Invalid server %s from %s for NICK %s", + parv[7], source_p->name, parv[1]); + sendto_one(client_p, ":%s KILL %s :%s (Server doesn't exist!)", + me.name, parv[1], me.name); + return; + } + + if (check_clean_nick(client_p, source_p, parv[1], server_p) || + check_clean_user(client_p, parv[1], parv[5], server_p) || + check_clean_host(client_p, parv[1], parv[6], server_p)) + return; + + if (IsServer(source_p)) + newts = atol(parv[3]); + if (IsServer(source_p) && parc == 10) + svsid = parv[8]; + } + else if (parc == 3) + { + if (IsServer(source_p)) + /* Servers can't change nicks.. */ + return; + + if (check_clean_nick(client_p, source_p, parv[1], + source_p->servptr)) + return; + + newts = atol(parv[2]); + } + + /* if the nick doesnt exist, allow it and process like normal */ + if ((target_p = hash_find_client(parv[1])) == NULL) + nick_from_server(client_p, source_p, parc, parv, newts, svsid, parv[1], parv[parc-1]); + else if (IsUnknown(target_p)) + { + /* we're not living in the past anymore, an unknown client is local only. */ + exit_client(target_p, &me, "Overridden"); + nick_from_server(client_p, source_p, parc, parv, newts, svsid, parv[1], parv[parc-1]); + } + else if (target_p == source_p) + { + if (strcmp(target_p->name, parv[1])) + nick_from_server(client_p, source_p, parc, parv, newts, svsid, parv[1], parv[parc-1]); + } + else + perform_nick_collides(source_p, client_p, target_p, parc, parv, + newts, svsid, parv[1], parv[parc-1], NULL); +} + + +/*! \brief UID command handler (called by servers) + * + * \param client_p Pointer to allocated Client struct with physical connection + * to this server, i.e. with an open socket connected. + * \param source_p Pointer to allocated Client struct from which the message + * originally comes from. This can be a local or remote client. + * \param parc Integer holding the number of supplied arguments. + * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL + * pointers. + * \note Valid arguments for this command are: + * + * server introducing new nick (without services support) + * - parv[0] = sender prefix + * - parv[1] = nickname + * - parv[2] = hop count + * - parv[3] = TS + * - parv[4] = umode + * - parv[5] = username + * - parv[6] = hostname + * - parv[7] = ip + * - parv[8] = uid + * - parv[9] = ircname (gecos) + * + * server introducing new nick (with services support) + * - parv[ 0] = sender prefix + * - parv[ 1] = nickname + * - parv[ 2] = hop count + * - parv[ 3] = TS + * - parv[ 4] = umode + * - parv[ 5] = username + * - parv[ 6] = hostname + * - parv[ 7] = ip + * - parv[ 8] = uid + * - parv[ 9] = services id (timestamp) + * - parv[10] = ircname (gecos) + */ +static void +ms_uid(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p = NULL; + time_t newts = 0; + const char *svsid = "0"; + + if (parc < 10 || EmptyString(parv[parc-1])) + return; + + if (check_clean_nick(client_p, source_p, parv[1], source_p) || + check_clean_user(client_p, parv[1], parv[5], source_p) || + check_clean_host(client_p, parv[1], parv[6], source_p)) + return; + + newts = atol(parv[3]); + svsid = parc == 11 ? parv[9] : "0"; + + /* + * if there is an ID collision, kill our client, and kill theirs. + * this may generate 401's, but it ensures that both clients always + * go, even if the other server refuses to do the right thing. + */ + if ((target_p = hash_find_id(parv[8])) != NULL) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "ID collision on %s(%s <- %s)(both killed)", + target_p->name, target_p->from->name, + client_p->name); + kill_client_ll_serv_butone(NULL, target_p, "%s (ID collision)", + me.name); + + ++ServerStats.is_kill; + AddFlag(target_p, FLAGS_KILLED); + exit_client(target_p, &me, "ID Collision"); + return; + } + + if ((target_p = hash_find_client(parv[1])) == NULL) + uid_from_server(client_p, source_p, parc, parv, newts, svsid, parv[1], parv[parc-1]); + else if (IsUnknown(target_p)) + { + exit_client(target_p, &me, "Overridden"); + uid_from_server(client_p, source_p, parc, parv, newts, svsid, parv[1], parv[parc-1]); + } + else + perform_nick_collides(source_p, client_p, target_p, parc, parv, newts, svsid, parv[1], + parv[parc-1], parv[8]); +} + +/* check_clean_nick() + * + * input - pointer to source + * - + * - nickname + * - truncated nickname + * - origin of client + * - pointer to server nick is coming from + * output - none + * side effects - if nickname is erroneous, or a different length to + * truncated nickname, return 1 + */ +static int +check_clean_nick(struct Client *client_p, struct Client *source_p, + char *nick, struct Client *server_p) +{ + /* the old code did some wacky stuff here, if the nick is invalid, kill it + * and dont bother messing at all + */ + if (!valid_nickname(nick, 0)) + { + ++ServerStats.is_kill; + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "Bad/long Nick: %s From: %s(via %s)", + nick, server_p->name, client_p->name); + + sendto_one(client_p, ":%s KILL %s :%s (Bad Nickname)", + me.name, nick, me.name); + + /* bad nick change */ + if (source_p != client_p) + { + kill_client_ll_serv_butone(client_p, source_p, + "%s (Bad Nickname)", + me.name); + AddFlag(source_p, FLAGS_KILLED); + exit_client(source_p, &me, "Bad Nickname"); + } + + return 1; + } + + return 0; +} + +/* check_clean_user() + * + * input - pointer to client sending data + * - nickname + * - username to check + * - origin of NICK + * output - none + * side effects - if username is erroneous, return 1 + */ +static int +check_clean_user(struct Client *client_p, char *nick, + char *user, struct Client *server_p) +{ + if (!clean_user_name(user)) + { + ++ServerStats.is_kill; + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "Bad/Long Username: %s Nickname: %s From: %s(via %s)", + user, nick, server_p->name, client_p->name); + sendto_one(client_p, ":%s KILL %s :%s (Bad Username)", + me.name, nick, me.name); + return 1; + } + + return 0; +} + +/* check_clean_host() + * + * input - pointer to client sending us data + * - nickname + * - hostname to check + * - source name + * output - none + * side effects - if hostname is erroneous, return 1 + */ +static int +check_clean_host(struct Client *client_p, char *nick, + char *host, struct Client *server_p) +{ + if (!clean_host_name(host)) + { + ++ServerStats.is_kill; + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "Bad/Long Hostname: %s Nickname: %s From: %s(via %s)", + host, nick, server_p->name, client_p->name); + sendto_one(client_p, ":%s KILL %s :%s (Bad Hostname)", + me.name, nick, me.name); + return 1; + } + + return 0; +} + +/* clean_user_name() + * + * input - username + * output - none + * side effects - walks through the username, returning 0 if erroneous + */ +static int +clean_user_name(const char *user) +{ + const char *p = user; + + assert(user && *user); + + for (; *p; ++p) + if (!IsUserChar(*p)) + return 0; + + return p - user <= USERLEN; +} + +/* clean_host_name() + * input - hostname + * output - none + * side effects - walks through the hostname, returning 0 if erroneous + */ +static int +clean_host_name(const char *host) +{ + const char *p = host; + + assert(host && *host); + + for (; *p; ++p) + if (!IsHostChar(*p)) + return 0; + + return p - host <= HOSTLEN; +} + +/* + * nick_from_server() + */ +static void +nick_from_server(struct Client *client_p, struct Client *source_p, int parc, + char *parv[], time_t newts, const char *svsid, char *nick, char *ngecos) +{ + int samenick = 0; + + if (IsServer(source_p)) + { + /* A server introducing a new client, change source */ + source_p = make_client(client_p); + dlinkAdd(source_p, &source_p->node, &global_client_list); + + if (parc > 2) + source_p->hopcount = atoi(parv[2]); + if (newts) + source_p->tsinfo = newts; + else + { + newts = source_p->tsinfo = CurrentTime; + ts_warn("Remote nick %s (%s) introduced without a TS", nick, parv[0]); + } + + strlcpy(source_p->svid, svsid, sizeof(source_p->svid)); + strlcpy(source_p->info, ngecos, sizeof(source_p->info)); + /* copy the nick in place */ + strlcpy(source_p->name, nick, sizeof(source_p->name)); + hash_add_client(source_p); + + if (parc > 8) + { + const char *m; + + /* parse usermodes */ + for (m = &parv[4][1]; *m; ++m) + { + unsigned int flag = user_modes[(unsigned char)*m]; + + if ((flag & UMODE_INVISIBLE) && !HasUMode(source_p, UMODE_INVISIBLE)) + ++Count.invisi; + if ((flag & UMODE_OPER) && !HasUMode(source_p, UMODE_OPER)) + ++Count.oper; + + source_p->umodes |= flag & SEND_UMODES; + } + + register_remote_user(source_p, parv[5], parv[6], + parv[7], ngecos); + return; + } + } + else if (source_p->name[0]) + { + samenick = !irccmp(source_p->name, nick); + + /* client changing their nick */ + if (!samenick) + { + DelUMode(source_p, UMODE_REGISTERED); + watch_check_hash(source_p, RPL_LOGOFF); + source_p->tsinfo = newts ? newts : CurrentTime; + } + + sendto_common_channels_local(source_p, 1, ":%s!%s@%s NICK :%s", + source_p->name,source_p->username, + source_p->host, nick); + + add_history(source_p, 1); + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s NICK %s :%lu", + ID(source_p), nick, (unsigned long)source_p->tsinfo); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s NICK %s :%lu", + source_p->name, nick, (unsigned long)source_p->tsinfo); + } + + /* set the new nick name */ + if (source_p->name[0]) + hash_del_client(source_p); + + strlcpy(source_p->name, nick, sizeof(source_p->name)); + hash_add_client(source_p); + + if (!samenick) + watch_check_hash(source_p, RPL_LOGON); +} + +/* + * client_from_server() + */ +static void +uid_from_server(struct Client *client_p, struct Client *source_p, int parc, + char *parv[], time_t newts, const char *svsid, char *nick, char *ugecos) +{ + const char *m = NULL; + const char *servername = source_p->name; + + source_p = make_client(client_p); + dlinkAdd(source_p, &source_p->node, &global_client_list); + + source_p->hopcount = atoi(parv[2]); + source_p->tsinfo = newts; + strlcpy(source_p->svid, svsid, sizeof(source_p->svid)); + + /* copy the nick in place */ + strcpy(source_p->name, nick); + strlcpy(source_p->id, parv[8], sizeof(source_p->id)); + strlcpy(source_p->sockhost, parv[7], sizeof(source_p->sockhost)); + strlcpy(source_p->info, ugecos, sizeof(source_p->info)); + + hash_add_client(source_p); + hash_add_id(source_p); + + /* parse usermodes */ + for (m = &parv[4][1]; *m; ++m) + { + unsigned int flag = user_modes[(unsigned char)*m]; + + if ((flag & UMODE_INVISIBLE) && !HasUMode(source_p, UMODE_INVISIBLE)) + ++Count.invisi; + if ((flag & UMODE_OPER) && !HasUMode(source_p, UMODE_OPER)) + ++Count.oper; + + source_p->umodes |= flag & SEND_UMODES; + } + + register_remote_user(source_p, parv[5], parv[6], + servername, ugecos); +} + +static void +perform_nick_collides(struct Client *source_p, struct Client *client_p, + struct Client *target_p, int parc, char *parv[], + time_t newts, const char *svsid, char *nick, char *gecos, char *uid) +{ + int sameuser; + + /* server introducing new nick */ + if (IsServer(source_p)) + { + /* if we dont have a ts, or their TS's are the same, kill both */ + if (!newts || !target_p->tsinfo || (newts == target_p->tsinfo)) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick collision on %s(%s <- %s)(both killed)", + target_p->name, target_p->from->name, + client_p->name); + + /* if we have a UID, issue a kill for it */ + if (uid) + sendto_one(client_p, ":%s KILL %s :%s (Nick collision (new))", + me.id, uid, me.name); + + kill_client_ll_serv_butone(NULL, target_p, + "%s (Nick collision (new))", + me.name); + ++ServerStats.is_kill; + sendto_one(target_p, form_str(ERR_NICKCOLLISION), + me.name, target_p->name, target_p->name); + + AddFlag(target_p, FLAGS_KILLED); + exit_client(target_p, &me, "Nick collision (new)"); + return; + } + /* the timestamps are different */ + else + { + sameuser = !irccmp(target_p->username, parv[5]) && + !irccmp(target_p->host, parv[6]); + + /* if the users are the same (loaded a client on a different server) + * and the new users ts is older, or the users are different and the + * new users ts is newer, ignore the new client and let it do the kill + */ + if ((sameuser && newts < target_p->tsinfo) || + (!sameuser && newts > target_p->tsinfo)) + { + if (uid) + sendto_one(client_p, ":%s KILL %s :%s (Nick collision (new))", + me.id, uid, me.name); + return; + } + else + { + if (sameuser) + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick collision on %s(%s <- %s)(older killed)", + target_p->name, target_p->from->name, + client_p->name); + else + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick collision on %s(%s <- %s)(newer killed)", + target_p->name, target_p->from->name, + client_p->name); + + ++ServerStats.is_kill; + sendto_one(target_p, form_str(ERR_NICKCOLLISION), + me.name, target_p->name, target_p->name); + + /* if it came from a LL server, itd have been source_p, + * so we dont need to mark target_p as known + */ + kill_client_ll_serv_butone(source_p, target_p, + "%s (Nick collision (new))", + me.name); + + AddFlag(target_p, FLAGS_KILLED); + exit_client(target_p, &me, "Nick collision"); + + if (!uid && (parc == 9 || parc == 10)) + nick_from_server(client_p, source_p, parc, parv, newts, svsid, nick, gecos); + else if (uid && (parc == 10 || parc == 11)) + uid_from_server(client_p, source_p, parc, parv, newts, svsid, nick, gecos); + + return; + } + } + } + + /* its a client changing nick and causing a collide */ + if (!newts || !target_p->tsinfo || (newts == target_p->tsinfo)) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick change collision from %s to %s(%s <- %s)(both killed)", + source_p->name, target_p->name, target_p->from->name, + client_p->name); + + sendto_one(target_p, form_str(ERR_NICKCOLLISION), me.name, + target_p->name, target_p->name); + + ++ServerStats.is_kill; + kill_client_ll_serv_butone(NULL, source_p, "%s (Nick change collision)", + me.name); + + ++ServerStats.is_kill; + kill_client_ll_serv_butone(NULL, target_p, "%s (Nick change collision)", + me.name); + + AddFlag(target_p, FLAGS_KILLED); + exit_client(target_p, &me, "Nick collision (new)"); + + AddFlag(source_p, FLAGS_KILLED); + exit_client(source_p, &me, "Nick collision (old)"); + return; + } + else + { + sameuser = !irccmp(target_p->username, source_p->username) && + !irccmp(target_p->host, source_p->host); + + if ((sameuser && newts < target_p->tsinfo) || + (!sameuser && newts > target_p->tsinfo)) + { + if (sameuser) + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick change collision from %s to %s(%s <- %s)(older killed)", + source_p->name, target_p->name, target_p->from->name, + client_p->name); + else + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick change collision from %s to %s(%s <- %s)(newer killed)", + source_p->name, target_p->name, target_p->from->name, + client_p->name); + + ++ServerStats.is_kill; + kill_client_ll_serv_butone(client_p, source_p, + "%s (Nick change collision)", + me.name); + + AddFlag(source_p, FLAGS_KILLED); + + if (sameuser) + exit_client(source_p, &me, "Nick collision (old)"); + else + exit_client(source_p, &me, "Nick collision (new)"); + return; + } + else + { + if (sameuser) + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick collision on %s(%s <- %s)(older killed)", + target_p->name, target_p->from->name, + client_p->name); + else + sendto_realops_flags(UMODE_ALL, L_ALL, + "Nick collision on %s(%s <- %s)(newer killed)", + target_p->name, target_p->from->name, + client_p->name); + + kill_client_ll_serv_butone(source_p, target_p, + "%s (Nick collision)", + me.name); + + ++ServerStats.is_kill; + sendto_one(target_p, form_str(ERR_NICKCOLLISION), + me.name, target_p->name, target_p->name); + + AddFlag(target_p, FLAGS_KILLED); + exit_client(target_p, &me, "Nick collision"); + } + } + + /* we should only ever call nick_from_server() here, as + * this is a client changing nick, not a new client + */ + nick_from_server(client_p, source_p, parc, parv, newts, svsid, nick, gecos); +} + +static struct Message nick_msgtab = { + "NICK", 0, 0, 0, MAXPARA, MFLG_SLOW, 0, + {mr_nick, m_nick, ms_nick, m_ignore, m_nick, m_ignore} +}; + +static struct Message uid_msgtab = { + "UID", 0, 0, 10, MAXPARA, MFLG_SLOW, 0, + {m_ignore, m_ignore, ms_uid, m_ignore, m_ignore, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&uid_msgtab); + mod_add_cmd(&nick_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&uid_msgtab); + mod_del_cmd(&nick_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_part.c b/modules/core/m_part.c new file mode 100644 index 0000000..3d1e83a --- /dev/null +++ b/modules/core/m_part.c @@ -0,0 +1,167 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_part.c: Parts a user from a channel. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "hash.h" +#include "irc_string.h" +#include "ircd.h" +#include "numeric.h" +#include "send.h" +#include "s_serv.h" +#include "parse.h" +#include "modules.h" +#include "conf.h" +#include "packet.h" + + +/* part_one_client() + * + * inputs - pointer to server + * - pointer to source client to remove + * - char pointer of name of channel to remove from + * output - none + * side effects - remove ONE client given the channel name + */ +static void +part_one_client(struct Client *client_p, struct Client *source_p, + const char *name, const char *reason) +{ + struct Channel *chptr = NULL; + struct Membership *ms = NULL; + + if ((chptr = hash_find_channel(name)) == NULL) + { + sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), + me.name, source_p->name, name); + return; + } + + if ((ms = find_channel_link(source_p, chptr)) == NULL) + { + sendto_one(source_p, form_str(ERR_NOTONCHANNEL), + me.name, source_p->name, name); + return; + } + + if (MyConnect(source_p) && !HasUMode(source_p, UMODE_OPER)) + check_spambot_warning(source_p, NULL); + + /* + * Remove user from the old channel (if any) + * only allow /part reasons in -m chans + */ + if (reason[0] && (!MyConnect(source_p) || + ((can_send(chptr, source_p, ms) && + (source_p->localClient->firsttime + ConfigFileEntry.anti_spam_exit_message_time) + < CurrentTime)))) + { + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s PART %s :%s", ID(source_p), chptr->chname, + reason); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s PART %s :%s", source_p->name, chptr->chname, + reason); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s PART %s :%s", + source_p->name, source_p->username, + source_p->host, chptr->chname, reason); + } + else + { + sendto_server(client_p, CAP_TS6, NOCAPS, + ":%s PART %s", ID(source_p), chptr->chname); + sendto_server(client_p, NOCAPS, CAP_TS6, + ":%s PART %s", source_p->name, chptr->chname); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s PART %s", + source_p->name, source_p->username, + source_p->host, chptr->chname); + } + + remove_user_from_channel(ms); +} + +/* +** m_part +** parv[0] = sender prefix +** parv[1] = channel +** parv[2] = reason +*/ +static void +m_part(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char *p = NULL, *name = NULL; + char reason[KICKLEN + 1] = { '\0' }; + + if (IsServer(source_p)) + return; + + if (EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "PART"); + return; + } + + if (parc > 2) + strlcpy(reason, parv[2], sizeof(reason)); + + /* Finish the flood grace period... */ + if (MyClient(source_p) && !IsFloodDone(source_p)) + flood_endgrace(source_p); + + for (name = strtoken(&p, parv[1], ","); name; + name = strtoken(&p, NULL, ",")) + part_one_client(client_p, source_p, name, reason); +} + +static struct Message part_msgtab = { + "PART", 0, 0, 2, MAXPARA, MFLG_SLOW, 0, + { m_unregistered, m_part, m_part, m_ignore, m_part, m_ignore } +}; + +static void +module_init(void) +{ + mod_add_cmd(&part_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&part_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_quit.c b/modules/core/m_quit.c new file mode 100644 index 0000000..ca73e2e --- /dev/null +++ b/modules/core/m_quit.c @@ -0,0 +1,98 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_quit.c: Makes a user quit from IRC. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "client.h" +#include "ircd.h" +#include "irc_string.h" +#include "s_serv.h" +#include "send.h" +#include "parse.h" +#include "modules.h" +#include "conf.h" + + +/* +** m_quit +** parv[0] = sender prefix +** parv[1] = comment +*/ +static void +m_quit(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char *comment = (parc > 1 && parv[1]) ? parv[1] : client_p->name; + char reason[KICKLEN + 1] = "Quit: "; + + if (*comment && (HasUMode(source_p, UMODE_OPER) || + (source_p->localClient->firsttime + ConfigFileEntry.anti_spam_exit_message_time) + < CurrentTime)) + strlcpy(reason+6, comment, sizeof(reason)-6); + + exit_client(source_p, source_p, reason); +} + +/* +** ms_quit +** parv[0] = sender prefix +** parv[1] = comment +*/ +static void +ms_quit(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char *comment = (parc > 1 && parv[1]) ? parv[1] : client_p->name; + + if (strlen(comment) > (size_t)KICKLEN) + comment[KICKLEN] = '\0'; + + exit_client(source_p, source_p, comment); +} + +static struct Message quit_msgtab = { + "QUIT", 0, 0, 0, MAXPARA, MFLG_SLOW, 0, + {m_quit, m_quit, ms_quit, m_ignore, m_quit, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&quit_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&quit_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_server.c b/modules/core/m_server.c new file mode 100644 index 0000000..6054a34 --- /dev/null +++ b/modules/core/m_server.c @@ -0,0 +1,624 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_server.c: Introduces a server. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "client.h" /* client struct */ +#include "event.h" +#include "hash.h" /* add_to_client_hash_table */ +#include "irc_string.h" +#include "ircd.h" /* me */ +#include "numeric.h" /* ERR_xxx */ +#include "conf.h" /* struct AccessItem */ +#include "log.h" /* log level defines */ +#include "s_serv.h" /* server_estab, check_server */ +#include "s_user.h" +#include "send.h" /* sendto_one */ +#include "parse.h" +#include "modules.h" + + +static void set_server_gecos(struct Client *, const char *); + +/* mr_server() + * parv[0] = sender prefix + * parv[1] = servername + * parv[2] = serverinfo/hopcount + * parv[3] = serverinfo + */ +static void +mr_server(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char *name; + struct Client *target_p; + int hop; + + if (EmptyString(parv[3])) + { + sendto_one(client_p, "ERROR :No servername"); + exit_client(client_p, client_p, "Wrong number of args"); + return; + } + + name = parv[1]; + hop = atoi(parv[2]); + + /* + * Reject a direct nonTS server connection if we're TS_ONLY -orabidoo + */ + if (!DoesTS(client_p)) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Unauthorized server connection attempt from %s: Non-TS server " + "for server %s", get_client_name(client_p, HIDE_IP), name); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Unauthorized server connection attempt from %s: Non-TS server " + "for server %s", get_client_name(client_p, MASK_IP), name); + exit_client(client_p, client_p, "Non-TS server"); + return; + } + + if (!valid_servname(name)) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Unauthorized server connection attempt from %s: Bogus server name " + "for server %s", get_client_name(client_p, HIDE_IP), name); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Unauthorized server connection attempt from %s: Bogus server name " + "for server %s", get_client_name(client_p, MASK_IP), name); + exit_client(client_p, client_p, "Bogus server name"); + return; + } + + /* Now we just have to call check_server and everything should + * be check for us... -A1kmm. + */ + switch (check_server(name, client_p)) + { + case -1: + if (ConfigFileEntry.warn_no_nline) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Unauthorized server connection attempt from %s: No entry for " + "servername %s", get_client_name(client_p, HIDE_IP), name); + + sendto_realops_flags(UMODE_ALL, L_OPER, + "Unauthorized server connection attempt from %s: No entry for " + "servername %s", get_client_name(client_p, MASK_IP), name); + } + + exit_client(client_p, client_p, "Invalid servername."); + return; + /* NOT REACHED */ + break; + + case -2: + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Unauthorized server connection attempt from %s: Bad password " + "for server %s", get_client_name(client_p, HIDE_IP), name); + + sendto_realops_flags(UMODE_ALL, L_OPER, + "Unauthorized server connection attempt from %s: Bad password " + "for server %s", get_client_name(client_p, MASK_IP), name); + + exit_client(client_p, client_p, "Invalid password."); + return; + /* NOT REACHED */ + break; + + case -3: + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Unauthorized server connection attempt from %s: Invalid host " + "for server %s", get_client_name(client_p, HIDE_IP), name); + + sendto_realops_flags(UMODE_ALL, L_OPER, + "Unauthorized server connection attempt from %s: Invalid host " + "for server %s", get_client_name(client_p, MASK_IP), name); + + exit_client(client_p, client_p, "Invalid host."); + return; + /* NOT REACHED */ + break; + } + + if ((client_p->id[0] && (target_p = hash_find_id(client_p->id))) + || (target_p = hash_find_server(name))) + { + /* This link is trying feed me a server that I already have + * access through another path -- multiple paths not accepted + * currently, kill this link immediately!! + * + * Rather than KILL the link which introduced it, KILL the + * youngest of the two links. -avalon + * + * Definitely don't do that here. This is from an unregistered + * connect - A1kmm. + */ + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Attempt to re-introduce server %s SID %s from %s", + name, client_p->id, + get_client_name(client_p, HIDE_IP)); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Attempt to re-introduce server %s SID %s from %s", + name, client_p->id, + get_client_name(client_p, MASK_IP)); + sendto_one(client_p, "ERROR :Server ID already exists."); + exit_client(client_p, client_p, "Server ID Exists"); + return; + } + + /* XXX If somehow there is a connect in progress and + * a connect comes in with same name toss the pending one, + * but only if it's not the same client! - Dianora + */ + if ((target_p = find_servconn_in_progress(name))) + if (target_p != client_p) + exit_client(target_p, &me, "Overridden"); + + /* if we are connecting (Handshake), we already have the name from the + * connect{} block in client_p->name + */ + strlcpy(client_p->name, name, sizeof(client_p->name)); + set_server_gecos(client_p, parv[3]); + client_p->hopcount = hop; + server_estab(client_p); +} + +/* ms_server() + * parv[0] = sender prefix + * parv[1] = servername + * parv[2] = serverinfo/hopcount + * parv[3] = serverinfo + */ +static void +ms_server(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char *name; + struct Client *target_p; + struct AccessItem *aconf; + int hop; + int hlined = 0; + int llined = 0; + dlink_node *ptr = NULL; + + /* Just to be sure -A1kmm. */ + if (!IsServer(source_p)) + return; + + if (EmptyString(parv[3])) + { + sendto_one(client_p, "ERROR :No servername"); + return; + } + + name = parv[1]; + hop = atoi(parv[2]); + + if (!valid_servname(name)) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s introduced server with bogus server name %s", + get_client_name(client_p, SHOW_IP), name); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s introduced server with bogus server name %s", + get_client_name(client_p, MASK_IP), name); + sendto_one(client_p, "ERROR :Bogus server name introduced"); + exit_client(client_p, &me, "Bogus server name intoduced"); + return; + } + + if ((target_p = hash_find_server(name))) + { + /* This link is trying feed me a server that I already have + * access through another path -- multiple paths not accepted + * currently, kill this link immediately!! + * + * Rather than KILL the link which introduced it, KILL the + * youngest of the two links. -avalon + * + * I think that we should exit the link itself, not the introducer, + * and we should always exit the most recently received(i.e. the + * one we are receiving this SERVER for. -A1kmm + * + * You *cant* do this, if you link somewhere, it bursts you a server + * that already exists, then sends you a client burst, you squit the + * server, but you keep getting the burst of clients on a server that + * doesnt exist, although ircd can handle it, its not a realistic + * solution.. --fl_ + */ + sendto_one(client_p, "ERROR :Server %s already exists", name); + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s cancelled, server %s already exists", + get_client_name(client_p, SHOW_IP), name); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s cancelled, server %s already exists", + client_p->name, name); + exit_client(client_p, &me, "Server Exists"); + return; + } + + /* XXX If somehow there is a connect in progress and + * a connect comes in with same name toss the pending one, + * but only if it's not the same client! - Dianora + */ + if ((target_p = find_servconn_in_progress(name))) + if (target_p != client_p) + exit_client(target_p, &me, "Overridden"); + + aconf = map_to_conf(client_p->localClient->confs.head->data); + + /* See if the newly found server is behind a guaranteed + * leaf. If so, close the link. + */ + DLINK_FOREACH(ptr, aconf->leaf_list.head) + if (match(ptr->data, name)) + { + llined = 1; + break; + } + + DLINK_FOREACH(ptr, aconf->hub_list.head) + if (match(ptr->data, name)) + { + hlined = 1; + break; + } + + /* Ok, this way this works is + * + * A server can have a CONF_HUB allowing it to introduce servers + * behind it. + * + * connect { + * name = "irc.bighub.net"; + * hub_mask="*"; + * ... + * + * That would allow "irc.bighub.net" to introduce anything it wanted.. + * + * However + * + * connect { + * name = "irc.somehub.fi"; + * hub_mask="*"; + * leaf_mask="*.edu"; + *... + * Would allow this server in finland to hub anything but + * .edu's + */ + + /* Ok, check client_p can hub the new server */ + if (!hlined) + { + /* OOOPs nope can't HUB */ + sendto_realops_flags(UMODE_ALL, L_ADMIN, "Non-Hub link %s introduced %s.", + get_client_name(client_p, HIDE_IP), name); + sendto_realops_flags(UMODE_ALL, L_OPER, "Non-Hub link %s introduced %s.", + get_client_name(client_p, MASK_IP), name); + exit_client(source_p, &me, "No matching hub_mask."); + return; + } + + /* Check for the new server being leafed behind this HUB */ + if (llined) + { + /* OOOPs nope can't HUB this leaf */ + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s introduced leafed server %s.", + get_client_name(client_p, HIDE_IP), name); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s introduced leafed server %s.", + get_client_name(client_p, MASK_IP), name); + /* If it is new, we are probably misconfigured, so split the + * non-hub server introducing this. Otherwise, split the new + * server. -A1kmm. + */ + /* wastes too much bandwidth, generates too many errors on + * larger networks, dont bother. --fl_ + */ + exit_client(client_p, &me, "Leafed Server."); + return; + } + + target_p = make_client(client_p); + make_server(target_p); + target_p->hopcount = hop; + target_p->servptr = source_p; + + strlcpy(target_p->name, name, sizeof(target_p->name)); + + set_server_gecos(target_p, parv[3]); + SetServer(target_p); + + if (HasFlag(source_p, FLAGS_SERVICE) || find_matching_name_conf(SERVICE_TYPE, target_p->name, NULL, NULL, 0)) + AddFlag(target_p, FLAGS_SERVICE); + + dlinkAdd(target_p, &target_p->node, &global_client_list); + dlinkAdd(target_p, make_dlink_node(), &global_serv_list); + dlinkAdd(target_p, &target_p->lnode, &target_p->servptr->serv->server_list); + + hash_add_client(target_p); + + sendto_server(client_p, NOCAPS, NOCAPS, ":%s SERVER %s %d :%s%s", + source_p->name, target_p->name, hop + 1, + IsHidden(target_p) ? "(H) " : "", target_p->info); + + sendto_realops_flags(UMODE_EXTERNAL, L_ALL, + "Server %s being introduced by %s", + target_p->name, source_p->name); +} + +/* ms_sid() + * parv[0] = sender prefix + * parv[1] = servername + * parv[2] = serverinfo/hopcount + * parv[3] = sid of new server + * parv[4] = serverinfo + */ +static void +ms_sid(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p; + struct AccessItem *aconf = NULL; + int hlined = 0; + int llined = 0; + dlink_node *ptr = NULL; + int hop; + + /* Just to be sure -A1kmm. */ + if (!IsServer(source_p)) + return; + + if (EmptyString(parv[4])) + { + sendto_one(client_p, "ERROR :No servername"); + return; + } + + hop = atoi(parv[2]); + + if (!valid_servname(parv[1])) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s introduced server with bogus server name %s", + get_client_name(client_p, SHOW_IP), parv[1]); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s introduced server with bogus server name %s", + get_client_name(client_p, MASK_IP), parv[1]); + sendto_one(client_p, "ERROR :Bogus server name introduced"); + exit_client(client_p, &me, "Bogus server name intoduced"); + return; + } + + if (!valid_sid(parv[3])) + { + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s introduced server with bogus server ID %s", + get_client_name(client_p, SHOW_IP), parv[3]); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s introduced server with bogus server ID %s", + get_client_name(client_p, MASK_IP), parv[3]); + sendto_one(client_p, "ERROR :Bogus server ID introduced"); + exit_client(client_p, &me, "Bogus server ID intoduced"); + return; + } + + /* collision on SID? */ + if ((target_p = hash_find_id(parv[3]))) + { + sendto_one(client_p, "ERROR :SID %s already exists", parv[3]); + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s cancelled, SID %s already exists", + get_client_name(client_p, SHOW_IP), parv[3]); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s cancelled, SID %s already exists", + client_p->name, parv[3]); + exit_client(client_p, &me, "SID Exists"); + return; + } + + /* collision on name? */ + if ((target_p = hash_find_server(parv[1]))) + { + sendto_one(client_p, "ERROR :Server %s already exists", parv[1]); + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s cancelled, server %s already exists", + get_client_name(client_p, SHOW_IP), parv[1]); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s cancelled, server %s already exists", + client_p->name, parv[1]); + exit_client(client_p, &me, "Server Exists"); + return; + } + + /* XXX If somehow there is a connect in progress and + * a connect comes in with same name toss the pending one, + * but only if it's not the same client! - Dianora + */ + if ((target_p = find_servconn_in_progress(parv[1]))) + if (target_p != client_p) + exit_client(target_p, &me, "Overridden"); + + aconf = map_to_conf(client_p->localClient->confs.head->data); + + /* See if the newly found server is behind a guaranteed + * leaf. If so, close the link. + */ + DLINK_FOREACH(ptr, aconf->leaf_list.head) + if (match(ptr->data, parv[1])) + { + llined = 1; + break; + } + + DLINK_FOREACH(ptr, aconf->hub_list.head) + if (match(ptr->data, parv[1])) + { + hlined = 1; + break; + } + + + /* Ok, this way this works is + * + * A server can have a CONF_HUB allowing it to introduce servers + * behind it. + * + * connect { + * name = "irc.bighub.net"; + * hub_mask="*"; + * ... + * + * That would allow "irc.bighub.net" to introduce anything it wanted.. + * + * However + * + * connect { + * name = "irc.somehub.fi"; + * hub_mask="*"; + * leaf_mask="*.edu"; + *... + * Would allow this server in finland to hub anything but + * .edu's + */ + + /* Ok, check client_p can hub the new server, and make sure it's not a LL */ + if (!hlined) + { + /* OOOPs nope can't HUB */ + sendto_realops_flags(UMODE_ALL, L_ADMIN, "Non-Hub link %s introduced %s.", + get_client_name(client_p, SHOW_IP), parv[1]); + sendto_realops_flags(UMODE_ALL, L_OPER, "Non-Hub link %s introduced %s.", + get_client_name(client_p, MASK_IP), parv[1]); + exit_client(source_p, &me, "No matching hub_mask."); + return; + } + + /* Check for the new server being leafed behind this HUB */ + if (llined) + { + /* OOOPs nope can't HUB this leaf */ + sendto_realops_flags(UMODE_ALL, L_ADMIN, + "Link %s introduced leafed server %s.", + get_client_name(client_p, SHOW_IP), parv[1]); + sendto_realops_flags(UMODE_ALL, L_OPER, + "Link %s introduced leafed server %s.", + get_client_name(client_p, MASK_IP), parv[1]); + exit_client(client_p, &me, "Leafed Server."); + return; + } + + target_p = make_client(client_p); + make_server(target_p); + target_p->hopcount = hop; + target_p->servptr = source_p; + + strlcpy(target_p->name, parv[1], sizeof(target_p->name)); + strlcpy(target_p->id, parv[3], sizeof(target_p->id)); + + set_server_gecos(target_p, parv[4]); + SetServer(target_p); + + if (HasFlag(source_p, FLAGS_SERVICE) || find_matching_name_conf(SERVICE_TYPE, target_p->name, NULL, NULL, 0)) + AddFlag(target_p, FLAGS_SERVICE); + + dlinkAdd(target_p, &target_p->node, &global_client_list); + dlinkAdd(target_p, make_dlink_node(), &global_serv_list); + dlinkAdd(target_p, &target_p->lnode, &target_p->servptr->serv->server_list); + + hash_add_client(target_p); + hash_add_id(target_p); + + sendto_server(client_p, CAP_TS6, NOCAPS, ":%s SID %s %d %s :%s%s", + ID_or_name(source_p, client_p), target_p->name, hop + 1, + target_p->id, IsHidden(target_p) ? "(H) " : "", target_p->info); + sendto_server(client_p, NOCAPS, CAP_TS6, ":%s SERVER %s %d :%s%s", + source_p->name, target_p->name, hop + 1, + IsHidden(target_p) ? "(H) " : "", target_p->info); + + sendto_realops_flags(UMODE_EXTERNAL, L_ALL, + "Server %s being introduced by %s", + target_p->name, source_p->name); +} + +/* set_server_gecos() + * + * input - pointer to client + * output - NONE + * side effects - servers gecos field is set + */ +static void +set_server_gecos(struct Client *client_p, const char *info) +{ + const char *s = info; + + /* check for (H) which is a hidden server */ + if (!strncmp(s, "(H) ", 4)) + { + SetHidden(client_p); + s = s + 4; + } + + if (!EmptyString(s)) + strlcpy(client_p->info, s, sizeof(client_p->info)); + else + strlcpy(client_p->info, "(Unknown Location)", sizeof(client_p->info)); +} + +static struct Message server_msgtab = { + "SERVER", 0, 0, 4, MAXPARA, MFLG_SLOW, 0, + {mr_server, m_registered, ms_server, m_ignore, m_registered, m_ignore} +}; + +static struct Message sid_msgtab = { + "SID", 0, 0, 5, MAXPARA, MFLG_SLOW, 0, + {rfc1459_command_send_error, m_ignore, ms_sid, m_ignore, m_ignore, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&sid_msgtab); + mod_add_cmd(&server_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&sid_msgtab); + mod_del_cmd(&server_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_sjoin.c b/modules/core/m_sjoin.c new file mode 100644 index 0000000..72f1bda --- /dev/null +++ b/modules/core/m_sjoin.c @@ -0,0 +1,850 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_sjoin.c: Joins a user to a channel. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "hash.h" +#include "irc_string.h" +#include "sprintf_irc.h" +#include "ircd.h" +#include "numeric.h" +#include "send.h" +#include "parse.h" +#include "modules.h" +#include "s_serv.h" +#include "conf.h" +#include "s_misc.h" + + +static char modebuf[MODEBUFLEN]; +static char parabuf[MODEBUFLEN]; +static char sendbuf[MODEBUFLEN]; +static const char *para[MAXMODEPARAMS]; +static char *mbuf; +static int pargs; + +static void set_final_mode(struct Mode *, struct Mode *); +static void remove_our_modes(struct Channel *, struct Client *); +static void remove_a_mode(struct Channel *, struct Client *, int, char); +static void remove_ban_list(struct Channel *, struct Client *, dlink_list *, char, int); + + +/* ms_sjoin() + * + * parv[0] - sender + * parv[1] - TS + * parv[2] - channel + * parv[3] - modes + n arguments (key and/or limit) + * parv[4+n] - flags+nick list (all in one parameter) + * + * process a SJOIN, taking the TS's into account to either ignore the + * incoming modes or undo the existing ones or merge them, and JOIN + * all the specified users while sending JOIN/MODEs to local clients + */ +static void +ms_sjoin(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Channel *chptr = NULL; + struct Client *target_p = NULL; + time_t newts; + time_t oldts; + time_t tstosend; + struct Mode mode, *oldmode; + int args = 0; + char keep_our_modes = 1; + char keep_new_modes = 1; + char have_many_nicks = 0; + int lcount; + char nick_prefix[4]; + char uid_prefix[4]; + char *np, *up; + int len_nick = 0; + int len_uid = 0; + int isnew = 0; + int buflen = 0; + int slen; + unsigned int fl; + char *s; + char *sptr; + char nick_buf[IRCD_BUFSIZE]; /* buffer for modes and prefix */ + char uid_buf[IRCD_BUFSIZE]; /* buffer for modes/prefixes for CAP_TS6 servers */ + char *nick_ptr, *uid_ptr; /* pointers used for making the two mode/prefix buffers */ + char *p; /* pointer used making sjbuf */ + dlink_node *m; + const char *servername = (ConfigServerHide.hide_servers || IsHidden(source_p)) ? + me.name : source_p->name; + + if (IsClient(source_p) || parc < 5) + return; + + if (!check_channel_name(parv[2], 0)) + { + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "*** Too long or invalid channel name from %s: %s", + client_p->name, parv[2]); + return; + } + + modebuf[0] = '\0'; + mbuf = modebuf; + pargs = 0; + newts = atol(parv[1]); + + mode.mode = 0; + mode.limit = 0; + mode.key[0] = '\0'; + + for (s = parv[3]; *s; ++s) + { + switch (*s) + { + case 't': + mode.mode |= MODE_TOPICLIMIT; + break; + case 'n': + mode.mode |= MODE_NOPRIVMSGS; + break; + case 's': + mode.mode |= MODE_SECRET; + break; + case 'm': + mode.mode |= MODE_MODERATED; + break; + case 'i': + mode.mode |= MODE_INVITEONLY; + break; + case 'p': + mode.mode |= MODE_PRIVATE; + break; + case 'r': + mode.mode |= MODE_REGISTERED; + break; + case 'O': + mode.mode |= MODE_OPERONLY; + break; + case 'S': + mode.mode |= MODE_SSLONLY; + break; + case 'k': + strlcpy(mode.key, parv[4 + args], sizeof(mode.key)); + args++; + + if (parc < 5 + args) + return; + break; + case 'l': + mode.limit = atoi(parv[4 + args]); + args++; + + if (parc < 5 + args) + return; + break; + } + } + + if ((chptr = hash_find_channel(parv[2])) == NULL) + { + isnew = 1; + chptr = make_channel(parv[2]); + } + + parabuf[0] = '\0'; + oldts = chptr->channelts; + oldmode = &chptr->mode; + + if (ConfigFileEntry.ignore_bogus_ts) + { + if (newts < 800000000) + { + sendto_realops_flags(UMODE_DEBUG, L_ALL, + "*** Bogus TS %lu on %s ignored from %s", + (unsigned long)newts, chptr->chname, + client_p->name); + + newts = (oldts == 0) ? 0 : 800000000; + } + } + else + { + if (!newts && !isnew && oldts) + { + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to 0", + me.name, chptr->chname, chptr->chname, (unsigned long)oldts); + sendto_realops_flags(UMODE_ALL, L_ALL, + "Server %s changing TS on %s from %lu to 0", + source_p->name, chptr->chname, (unsigned long)oldts); + } + } + + if (isnew) + chptr->channelts = tstosend = newts; + else if (newts == 0 || oldts == 0) + chptr->channelts = tstosend = 0; + else if (newts == oldts) + tstosend = oldts; + else if (newts < oldts) + { + keep_our_modes = 0; + chptr->channelts = tstosend = newts; + } + else + { + keep_new_modes = 0; + tstosend = oldts; + } + + if (!keep_new_modes) + mode = *oldmode; + else if (keep_our_modes) + { + mode.mode |= oldmode->mode; + if (oldmode->limit > mode.limit) + mode.limit = oldmode->limit; + if (strcmp(mode.key, oldmode->key) < 0) + strcpy(mode.key, oldmode->key); + } + set_final_mode(&mode, oldmode); + chptr->mode = mode; + + /* Lost the TS, other side wins, so remove modes on this side */ + if (!keep_our_modes) + { + remove_our_modes(chptr, source_p); + + if (chptr->topic[0]) + { + set_channel_topic(chptr, "", "", 0); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s TOPIC %s :", + (IsHidden(source_p) || + ConfigServerHide.hide_servers) ? + me.name : source_p->name, chptr->chname); + } + + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s NOTICE %s :*** Notice -- TS for %s changed from %lu to %lu", + me.name, chptr->chname, chptr->chname, + (unsigned long)oldts, (unsigned long)newts); + } + + if (*modebuf != '\0') + { + /* This _SHOULD_ be to ALL_MEMBERS + * It contains only +imnpstlk, etc */ + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s %s %s", + servername, chptr->chname, modebuf, parabuf); + } + + if (parv[3][0] != '0' && keep_new_modes) + { + channel_modes(chptr, source_p, modebuf, parabuf); + } + else + { + modebuf[0] = '0'; + modebuf[1] = '\0'; + } + + buflen = ircsprintf(nick_buf, ":%s SJOIN %lu %s %s %s:", + source_p->name, (unsigned long)tstosend, + chptr->chname, modebuf, parabuf); + nick_ptr = nick_buf + buflen; + + buflen = ircsprintf(uid_buf, ":%s SJOIN %lu %s %s %s:", + ID(source_p), (unsigned long)tstosend, + chptr->chname, modebuf, parabuf); + uid_ptr = uid_buf + buflen; + + /* check we can fit a nick on the end, as well as \r\n and a prefix " + * @%+", and a space. + */ + if (buflen >= (IRCD_BUFSIZE - IRCD_MAX(NICKLEN, IDLEN) - 2 - 3 - 1)) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "Long SJOIN from server: %s(via %s) (ignored)", + source_p->name, client_p->name); + return; + } + + mbuf = modebuf; + sendbuf[0] = '\0'; + pargs = 0; + + *mbuf++ = '+'; + + s = parv[args + 4]; + while (*s == ' ') + s++; + if ((p = strchr(s, ' ')) != NULL) + { + *p++ = '\0'; + while (*p == ' ') + p++; + have_many_nicks = *p; + } + + while (*s) + { + int valid_mode = 1; + fl = 0; + + do + { + switch (*s) + { + case '@': + fl |= CHFL_CHANOP; + s++; + break; +#ifdef HALFOPS + case '%': + fl |= CHFL_HALFOP; + s++; + break; +#endif + case '+': + fl |= CHFL_VOICE; + s++; + break; + default: + valid_mode = 0; + break; + } + } while (valid_mode); + + target_p = find_chasing(client_p, source_p, s, NULL); + + /* + * if the client doesnt exist, or if its fake direction/server, skip. + * we cannot send ERR_NOSUCHNICK here because if its a UID, we cannot + * lookup the nick, and its better to never send the numeric than only + * sometimes. + */ + if (target_p == NULL || + target_p->from != client_p || + !IsClient(target_p)) + { + goto nextnick; + } + + len_nick = strlen(target_p->name); + len_uid = strlen(ID(target_p)); + + np = nick_prefix; + up = uid_prefix; + + if (keep_new_modes) + { + if (fl & CHFL_CHANOP) + { + *np++ = '@'; + *up++ = '@'; + len_nick++; + len_uid++; + } +#ifdef HALFOPS + if (fl & CHFL_HALFOP) + { + *np++ = '%'; + *up++ = '%'; + len_nick++; + len_uid++; + } +#endif + if (fl & CHFL_VOICE) + { + *np++ = '+'; + *up++ = '+'; + len_nick++; + len_uid++; + } + } + else + { + if (fl & (CHFL_CHANOP|CHFL_HALFOP)) + fl = CHFL_DEOPPED; + else + fl = 0; + } + *np = *up = '\0'; + + if ((nick_ptr - nick_buf + len_nick) > (IRCD_BUFSIZE - 2)) + { + sendto_server(client_p, 0, CAP_TS6, "%s", nick_buf); + + buflen = ircsprintf(nick_buf, ":%s SJOIN %lu %s %s %s:", + source_p->name, (unsigned long)tstosend, + chptr->chname, modebuf, parabuf); + nick_ptr = nick_buf + buflen; + } + + nick_ptr += ircsprintf(nick_ptr, "%s%s ", nick_prefix, target_p->name); + + if ((uid_ptr - uid_buf + len_uid) > (IRCD_BUFSIZE - 2)) + { + sendto_server(client_p, CAP_TS6, 0, "%s", uid_buf); + + buflen = ircsprintf(uid_buf, ":%s SJOIN %lu %s %s %s:", + ID(source_p), (unsigned long)tstosend, + chptr->chname, modebuf, parabuf); + uid_ptr = uid_buf + buflen; + } + + uid_ptr += ircsprintf(uid_ptr, "%s%s ", uid_prefix, ID(target_p)); + + if (!IsMember(target_p, chptr)) + { + add_user_to_channel(chptr, target_p, fl, !have_many_nicks); + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s!%s@%s JOIN :%s", + target_p->name, target_p->username, + target_p->host, chptr->chname); + } + + if (fl & CHFL_CHANOP) + { + *mbuf++ = 'o'; + para[pargs++] = target_p->name; + + if (pargs >= MAXMODEPARAMS) + { + /* + * Ok, the code is now going to "walk" through + * sendbuf, filling in para strings. So, I will use sptr + * to point into the sendbuf. + * Notice, that ircsprintf() returns the number of chars + * successfully inserted into string. + * - Dianora + */ + + sptr = sendbuf; + *mbuf = '\0'; + for(lcount = 0; lcount < MAXMODEPARAMS; lcount++) + { + slen = ircsprintf(sptr, " %s", para[lcount]); /* see? */ + sptr += slen; /* ready for next */ + } + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s %s%s", + servername, chptr->chname, modebuf, sendbuf); + mbuf = modebuf; + *mbuf++ = '+'; + + sendbuf[0] = '\0'; + pargs = 0; + } + } +#ifdef HALFOPS + if (fl & CHFL_HALFOP) + { + *mbuf++ = 'h'; + para[pargs++] = target_p->name; + + if (pargs >= MAXMODEPARAMS) + { + sptr = sendbuf; + *mbuf = '\0'; + for(lcount = 0; lcount < MAXMODEPARAMS; lcount++) + { + slen = ircsprintf(sptr, " %s", para[lcount]); + sptr += slen; + } + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s %s%s", + servername, chptr->chname, modebuf, sendbuf); + + mbuf = modebuf; + *mbuf++ = '+'; + + sendbuf[0] = '\0'; + pargs = 0; + } + } +#endif + if (fl & CHFL_VOICE) + { + *mbuf++ = 'v'; + para[pargs++] = target_p->name; + + if (pargs >= MAXMODEPARAMS) + { + sptr = sendbuf; + *mbuf = '\0'; + for (lcount = 0; lcount < MAXMODEPARAMS; lcount++) + { + slen = ircsprintf(sptr, " %s", para[lcount]); + sptr += slen; + } + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s %s%s", + servername, chptr->chname, modebuf, sendbuf); + + mbuf = modebuf; + *mbuf++ = '+'; + + sendbuf[0] = '\0'; + pargs = 0; + } + } + + nextnick: + if ((s = p) == NULL) + break; + while (*s == ' ') + s++; + if ((p = strchr(s, ' ')) != NULL) + { + *p++ = 0; + while (*p == ' ') + p++; + } + } + + *mbuf = '\0'; + *(nick_ptr - 1) = '\0'; + *(uid_ptr - 1) = '\0'; + + /* + * checking for lcount < MAXMODEPARAMS at this time is wrong + * since the code has already verified above that pargs < MAXMODEPARAMS + * checking for para[lcount] != '\0' is also wrong, since + * there is no place where para[lcount] is set! + * - Dianora + */ + + if (pargs != 0) + { + sptr = sendbuf; + + for (lcount = 0; lcount < pargs; lcount++) + { + slen = ircsprintf(sptr, " %s", para[lcount]); + sptr += slen; + } + + sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s MODE %s %s%s", + servername, chptr->chname, modebuf, sendbuf); + } + + /* If this happens, its the result of a malformed SJOIN + * a remnant from the old persistent channel code. *sigh* + * Or it could be the result of a client just leaving + * and leaving us with a channel formed just as the client parts. + * - Dianora + */ + + if ((dlink_list_length(&chptr->members) == 0) && isnew) + { + destroy_channel(chptr); + return; + } + + if (parv[4 + args][0] == '\0') + return; + + /* relay the SJOIN to other servers */ + DLINK_FOREACH(m, serv_list.head) + { + target_p = m->data; + + if (target_p == client_p) + continue; + + if (IsCapable(target_p, CAP_TS6)) + sendto_one(target_p, "%s", uid_buf); + else + sendto_one(target_p, "%s", nick_buf); + } + + if (HasID(source_p) && !keep_our_modes) + { + if (dlink_list_length(&chptr->banlist) > 0) + remove_ban_list(chptr, client_p, &chptr->banlist, + 'b', NOCAPS); + + if (dlink_list_length(&chptr->exceptlist) > 0) + remove_ban_list(chptr, client_p, &chptr->exceptlist, + 'e', CAP_EX); + + if (dlink_list_length(&chptr->invexlist) > 0) + remove_ban_list(chptr, client_p, &chptr->invexlist, + 'I', CAP_IE); + clear_ban_cache(chptr); + } +} + +/* set_final_mode + * + * inputs - channel mode + * - old channel mode + * output - NONE + * side effects - walk through all the channel modes turning off modes + * that were on in oldmode but aren't on in mode. + * Then walk through turning on modes that are on in mode + * but were not set in oldmode. + */ +static void +set_final_mode(struct Mode *mode, struct Mode *oldmode) +{ + const struct mode_letter *tab; + char *pbuf = parabuf; + int len; + + *mbuf++ = '-'; + + for (tab = chan_modes; tab->letter; ++tab) + { + if ((tab->mode & oldmode->mode) && + !(tab->mode & mode->mode)) + *mbuf++ = tab->letter; + } + + if (oldmode->limit != 0 && mode->limit == 0) + *mbuf++ = 'l'; + + if (oldmode->key[0] && !mode->key[0]) + { + *mbuf++ = 'k'; + len = ircsprintf(pbuf, "%s ", oldmode->key); + pbuf += len; + pargs++; + } + + if (*(mbuf-1) == '-') + *(mbuf-1) = '+'; + else + *mbuf++ = '+'; + + for (tab = chan_modes; tab->letter; ++tab) + { + if ((tab->mode & mode->mode) && + !(tab->mode & oldmode->mode)) + *mbuf++ = tab->letter; + } + + if (mode->limit != 0 && oldmode->limit != mode->limit) + { + *mbuf++ = 'l'; + len = ircsprintf(pbuf, "%d ", mode->limit); + pbuf += len; + pargs++; + } + + if (mode->key[0] && strcmp(oldmode->key, mode->key)) + { + *mbuf++ = 'k'; + len = ircsprintf(pbuf, "%s ", mode->key); + pbuf += len; + pargs++; + } + if (*(mbuf-1) == '+') + *(mbuf-1) = '\0'; + else + *mbuf = '\0'; +} + +/* remove_our_modes() + * + * inputs - pointer to channel to remove modes from + * - client pointer + * output - NONE + * side effects - Go through the local members, remove all their + * chanop modes etc., this side lost the TS. + */ +static void +remove_our_modes(struct Channel *chptr, struct Client *source_p) +{ + remove_a_mode(chptr, source_p, CHFL_CHANOP, 'o'); +#ifdef HALFOPS + remove_a_mode(chptr, source_p, CHFL_HALFOP, 'h'); +#endif + remove_a_mode(chptr, source_p, CHFL_VOICE, 'v'); +} + +/* remove_a_mode() + * + * inputs - pointer to channel + * - server or client removing the mode + * - mask o/h/v mask to be removed + * - flag o/h/v to be removed + * output - NONE + * side effects - remove ONE mode from all members of a channel + */ +static void +remove_a_mode(struct Channel *chptr, struct Client *source_p, + int mask, char flag) +{ + dlink_node *ptr; + struct Membership *ms; + char lmodebuf[MODEBUFLEN]; + char *sp=sendbuf; + const char *lpara[MAXMODEPARAMS]; + int count = 0; + int i; + int l; + + mbuf = lmodebuf; + *mbuf++ = '-'; + *sp = '\0'; + + DLINK_FOREACH(ptr, chptr->members.head) + { + ms = ptr->data; + + if ((ms->flags & mask) == 0) + continue; + + ms->flags &= ~mask; + + lpara[count++] = ms->client_p->name; + + *mbuf++ = flag; + + if (count >= MAXMODEPARAMS) + { + for(i = 0; i < MAXMODEPARAMS; i++) + { + l = ircsprintf(sp, " %s", lpara[i]); + sp += l; + } + + *mbuf = '\0'; + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s MODE %s %s%s", + (IsHidden(source_p) || + ConfigServerHide.hide_servers) ? + me.name : source_p->name, + chptr->chname, lmodebuf, sendbuf); + mbuf = lmodebuf; + *mbuf++ = '-'; + count = 0; + sp = sendbuf; + *sp = '\0'; + } + } + + if (count != 0) + { + *mbuf = '\0'; + for(i = 0; i < count; i++) + { + l = ircsprintf(sp, " %s", lpara[i]); + sp += l; + } + sendto_channel_local(ALL_MEMBERS, 0, chptr, + ":%s MODE %s %s%s", + (IsHidden(source_p) || ConfigServerHide.hide_servers) ? + me.name : source_p->name, + chptr->chname, lmodebuf, sendbuf); + } +} + +/* remove_ban_list() + * + * inputs - channel, source, list to remove, char of mode, caps required + * outputs - none + * side effects - given ban list is removed, modes are sent to local clients and + * non-ts6 servers linked through another uplink other than source_p + */ +static void +remove_ban_list(struct Channel *chptr, struct Client *source_p, + dlink_list *list, char c, int cap) +{ + char lmodebuf[MODEBUFLEN]; + char lparabuf[IRCD_BUFSIZE]; + struct Ban *banptr = NULL; + dlink_node *ptr = NULL; + dlink_node *next_ptr = NULL; + char *pbuf = NULL; + int count = 0; + int cur_len, mlen, plen; + + pbuf = lparabuf; + + cur_len = mlen = ircsprintf(lmodebuf, ":%s MODE %s -", + source_p->name, chptr->chname); + mbuf = lmodebuf + mlen; + + DLINK_FOREACH_SAFE(ptr, next_ptr, list->head) + { + banptr = ptr->data; + + plen = banptr->len + 4; /* another +b and "!@ " */ + if (count >= MAXMODEPARAMS || + (cur_len + 1 /* space between */ + (plen - 1)) > IRCD_BUFSIZE - 2) + { + /* NUL-terminate and remove trailing space */ + *mbuf = *(pbuf - 1) = '\0'; + sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", + lmodebuf, lparabuf); + sendto_server(source_p, cap, CAP_TS6, + "%s %s", lmodebuf, lparabuf); + + cur_len = mlen; + mbuf = lmodebuf + mlen; + pbuf = lparabuf; + count = 0; + } + + *mbuf++ = c; + cur_len += plen; + pbuf += ircsprintf(pbuf, "%s!%s@%s ", banptr->name, banptr->username, + banptr->host); + ++count; + + remove_ban(banptr, list); + } + + *mbuf = *(pbuf - 1) = '\0'; + sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", lmodebuf, lparabuf); + sendto_server(source_p, cap, CAP_TS6, + "%s %s", lmodebuf, lparabuf); +} + +static struct Message sjoin_msgtab = { + "SJOIN", 0, 0, 0, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_ignore, ms_sjoin, m_ignore, m_ignore, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&sjoin_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&sjoin_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; diff --git a/modules/core/m_squit.c b/modules/core/m_squit.c new file mode 100644 index 0000000..0995c47 --- /dev/null +++ b/modules/core/m_squit.c @@ -0,0 +1,184 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * m_squit.c: Makes a server quit. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * 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 + * + * $Id$ + */ + +#include "stdinc.h" +#include "list.h" +#include "client.h" +#include "hash.h" +#include "irc_string.h" +#include "ircd.h" +#include "numeric.h" +#include "conf.h" +#include "log.h" +#include "s_serv.h" +#include "send.h" +#include "parse.h" +#include "modules.h" + + +/* mo_squit - SQUIT message handler + * parv[0] = sender prefix + * parv[1] = server name + * parv[2] = comment + */ +static void +mo_squit(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p = NULL; + struct Client *p; + dlink_node *ptr; + char *comment; + const char *server; + char def_reason[] = "No reason"; + + if (parc < 2 || EmptyString(parv[1])) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "SQUIT"); + return; + } + + server = parv[1]; + + /* The following allows wild cards in SQUIT. Only + * useful when the command is issued by an oper. + */ + DLINK_FOREACH(ptr, global_serv_list.head) + { + p = ptr->data; + + if (IsServer(p) || IsMe(p)) + { + if (match(server, p->name)) + { + target_p = p; + break; + } + } + } + + if ((target_p == NULL) || IsMe(target_p)) + { + sendto_one(source_p, form_str(ERR_NOSUCHSERVER), + me.name, source_p->name, server); + return; + } + + if (!MyConnect(target_p) && !HasOFlag(source_p, OPER_FLAG_REMOTE)) + { + sendto_one(source_p, form_str(ERR_NOPRIVILEGES), + me.name, source_p->name); + return; + } + + comment = (parc > 2 && parv[2]) ? parv[2] : def_reason; + + if (strlen(comment) > (size_t)REASONLEN) + comment[REASONLEN] = '\0'; + + if (MyConnect(target_p)) + { + sendto_realops_flags(UMODE_ALL, L_ALL, "Received SQUIT %s from %s (%s)", + target_p->name, get_client_name(source_p, HIDE_IP), comment); + ilog(LOG_TYPE_IRCD, "Received SQUIT %s from %s (%s)", + target_p->name, get_client_name(source_p, HIDE_IP), comment); + } + + exit_client(target_p, source_p, comment); +} + +/** NOTE: I removed wildcard lookups here, because a wildcarded + ** SQUIT should/can never happen in ms_squit. -Michael + **/ + +/* ms_squit - SQUIT message handler + * parv[0] = sender prefix + * parv[1] = server name + * parv[2] = comment + */ +static void +ms_squit(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + struct Client *target_p = NULL; + const char *comment = NULL; + + if (parc < 2 || EmptyString(parv[parc - 1])) + return; + + if ((target_p = hash_find_server(parv[1])) == NULL) + return; + + if (!IsServer(target_p) && !IsMe(target_p)) + return; + + if (IsMe(target_p)) + target_p = client_p; + + comment = (parc > 2 && parv[parc - 1]) ? parv[parc - 1] : client_p->name; + + if (MyConnect(target_p)) + { + sendto_wallops_flags(UMODE_WALLOP, &me, "Remote SQUIT %s from %s (%s)", + target_p->name, source_p->name, comment); + sendto_server(NULL, CAP_TS6, NOCAPS, + ":%s WALLOPS :Remote SQUIT %s from %s (%s)", + me.id, target_p->name, source_p->name, comment); + sendto_server(NULL, NOCAPS, CAP_TS6, + ":%s WALLOPS :Remote SQUIT %s from %s (%s)", + me.name, target_p->name, source_p->name, comment); + ilog(LOG_TYPE_IRCD, "SQUIT From %s : %s (%s)", source_p->name, + target_p->name, comment); + } + + exit_client(target_p, source_p, comment); +} + +static struct Message squit_msgtab = { + "SQUIT", 0, 0, 1, MAXPARA, MFLG_SLOW, 0, + {m_unregistered, m_not_oper, ms_squit, m_ignore, mo_squit, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&squit_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&squit_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = MODULE_FLAG_CORE +}; |