| summaryrefslogtreecommitdiff | 
diff options
32 files changed, 2129 insertions, 0 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4540388 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) + +project("JabberHive - IRC Gateway") + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") + +include(FindPkgConfig) +include(FindPkgConfig) + +add_subdirectory(src) +add_definitions(-D_POSIX_SOURCE) +add_definitions(-D_POSIX_C_SOURCE=200809L) +find_package(LibIRCClient REQUIRED) + +if(CMAKE_COMPILER_IS_GNUCC) +   set(CMAKE_C_FLAGS "-g -Wall -Wpedantic -Wconversion") +   #message(STATUS "GNUCC detected. Adding '-O3' parameter.") +   #set(CMAKE_C_FLAGS "-O2") +endif() + +# ${SRC_FILES} is recursively defined in the subdirectories. +# Each subdirectory only adds the source files that are present at its level. +add_executable(jabberhive-irc ${SRC_FILES}) +set_property(TARGET jabberhive-irc PROPERTY C_STANDARD 99) +set_property(TARGET jabberhive-irc PROPERTY C_STANDARD_REQUIRED ON) +target_link_libraries(jabberhive-irc ${LibIRCClient_LIBRARIES}) + +## OPTION HANDLING ############################################################# +# TODO @@ -0,0 +1,27 @@ +Copyright (c) 2017, NathanaĆ«l Sensfelder +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +  list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, +  this list of conditions and the following disclaimer in the documentation +  and/or other materials provided with the distribution. + +* Neither the name of JabberHive nor the names of its contributors may be used +  to endorse or promote products derived from this software without specific +  prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..701b909 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +## What is JabberHive? +JabberHive is a modular Reply Bot system. All "modules" are in fact separate +programs linked together using the JabberHive Protocol. Please refer to the +protocol for more information. + +## Component Description +* IRC Gateway for a JabberHive network. + +## JabberHive Protocol Compatibility +* **Protocol Version(s):** 1. +* **Inbound Connections:** None. +* **Outbound Connections:** Single. +* **Pipelining:** No. +* **Behavior:** Gateway. + + +## Dependencies +- POSIX compliant OS. +- C compiler (with C99 support). +- CMake. +- libircclient diff --git a/cmake_modules/FindLibIRCClient.cmake b/cmake_modules/FindLibIRCClient.cmake new file mode 100644 index 0000000..6884b17 --- /dev/null +++ b/cmake_modules/FindLibIRCClient.cmake @@ -0,0 +1,56 @@ +# Taken from https://github.com/sgielen/libdazeus-irc + + +#Copyright (c) Sjors Gielen, 2010-2012 +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +#modification, are permitted provided that the following conditions are met: +#    * Redistributions of source code must retain the above copyright +#      notice, this list of conditions and the following disclaimer. +#    * Redistributions in binary form must reproduce the above copyright +#      notice, this list of conditions and the following disclaimer in the +#      documentation and/or other materials provided with the distribution. +#    * Neither the name of the DaVinci or DaZeus team nor the +#      names of its contributors may be used to endorse or promote products +#      derived from this software without specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED. IN NO EVENT SHALL SJORS GIELEN OR CONTRIBUTORS BE LIABLE FOR ANY +#DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Once done, this will define +# +#  LibIRCClient_FOUND - system has LibIRCClient +#  LibIRCClient_INCLUDE_DIRS - the LibIRCClient include directories +#  LibIRCClient_LIBRARIES - link these to use LibIRCClient + +include(LibFindMacros) + +# Include dir +find_path(LibIRCClient_INCLUDE_DIR +  NAMES libircclient.h +  PATHS ${LibIRCClient_PKGCONF_INCLUDE_DIRS} +      /usr/include /sw/include /usr/local/include +      /usr/include/libircclient /sw/include/libircclient +      /usr/local/include/libircclient +) + +# Finally the library itself +find_library(LibIRCClient_LIBRARY +  NAMES ircclient +  PATHS ${LibIRCClient_PKGCONF_LIBRARY_DIRS} +      /usr/lib /lib /sw/lib /usr/local/lib +) + +set(LibIRCClient_PROCESS_INCLUDES LibIRCClient_INCLUDE_DIR) +set(LibIRCClient_PROCESS_LIBS LibIRCClient_LIBRARY) +libfind_process(LibIRCClient) diff --git a/cmake_modules/LibFindMacros.cmake b/cmake_modules/LibFindMacros.cmake new file mode 100644 index 0000000..ea3568b --- /dev/null +++ b/cmake_modules/LibFindMacros.cmake @@ -0,0 +1,127 @@ +# Taken from https://github.com/sgielen/libdazeus-irc + + +#Copyright (c) Sjors Gielen, 2010-2012 +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +#modification, are permitted provided that the following conditions are met: +#    * Redistributions of source code must retain the above copyright +#      notice, this list of conditions and the following disclaimer. +#    * Redistributions in binary form must reproduce the above copyright +#      notice, this list of conditions and the following disclaimer in the +#      documentation and/or other materials provided with the distribution. +#    * Neither the name of the DaVinci or DaZeus team nor the +#      names of its contributors may be used to endorse or promote products +#      derived from this software without specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED. IN NO EVENT SHALL SJORS GIELEN OR CONTRIBUTORS BE LIABLE FOR ANY +#DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) +  set (LIBFIND_PACKAGE_ARGS ${ARGN}) +  if (${PREFIX}_FIND_QUIETLY) +    set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) +  endif (${PREFIX}_FIND_QUIETLY) +  if (${PREFIX}_FIND_REQUIRED) +    set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) +  endif (${PREFIX}_FIND_REQUIRED) +  find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) +  if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +    include(UsePkgConfig) +    pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) +  else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +    find_package(PkgConfig) +    if (PKG_CONFIG_FOUND) +      pkg_check_modules(${PREFIX} ${PKGNAME}) +    endif (PKG_CONFIG_FOUND) +  endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) +  # Skip processing if already processed during this run +  if (NOT ${PREFIX}_FOUND) +    # Start with the assumption that the library was found +    set (${PREFIX}_FOUND TRUE) + +    # Process all includes and set _FOUND to false if any are missing +    foreach (i ${${PREFIX}_PROCESS_INCLUDES}) +      if (${i}) +        set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) +        mark_as_advanced(${i}) +      else (${i}) +        set (${PREFIX}_FOUND FALSE) +      endif (${i}) +    endforeach (i) + +    # Process all libraries and set _FOUND to false if any are missing +    foreach (i ${${PREFIX}_PROCESS_LIBS}) +      if (${i}) +        set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) +        mark_as_advanced(${i}) +      else (${i}) +        set (${PREFIX}_FOUND FALSE) +      endif (${i}) +    endforeach (i) + +    # Print message and/or exit on fatal error +    if (${PREFIX}_FOUND) +      if (NOT ${PREFIX}_FIND_QUIETLY) +        message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") +      endif (NOT ${PREFIX}_FIND_QUIETLY) +    else (${PREFIX}_FOUND) +      if (${PREFIX}_FIND_REQUIRED) +        foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) +          message("${i}=${${i}}") +        endforeach (i) +        message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") +      endif (${PREFIX}_FIND_REQUIRED) +    endif (${PREFIX}_FOUND) +  endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) +  set(TMP "") +  if(MSVC80) +    set(TMP -vc80) +  endif(MSVC80) +  if(MSVC90) +    set(TMP -vc90) +  endif(MSVC90) +  set(${PREFIX}_LIBNAMES ${basename}${TMP}) +  if(${ARGC} GREATER 2) +    set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) +    string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) +    set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) +  endif(${ARGC} GREATER 2) +  find_library(${PREFIX}_LIBRARY +    NAMES ${${PREFIX}_LIBNAMES} +    PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} +  ) +endmacro(libfind_library) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..5d1adf6 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,11 @@ +add_subdirectory(error) +add_subdirectory(parameters) +add_subdirectory(meta_net) +add_subdirectory(irc) + +set( +   SRC_FILES ${SRC_FILES} +   ${CMAKE_CURRENT_SOURCE_DIR}/main.c +) + +set(SRC_FILES ${SRC_FILES} PARENT_SCOPE) diff --git a/src/error/CMakeLists.txt b/src/error/CMakeLists.txt new file mode 100644 index 0000000..fa07534 --- /dev/null +++ b/src/error/CMakeLists.txt @@ -0,0 +1,6 @@ +set( +   SRC_FILES ${SRC_FILES} +) + +set(SRC_FILES ${SRC_FILES} PARENT_SCOPE) + diff --git a/src/error/error.h b/src/error/error.h new file mode 100644 index 0000000..145c838 --- /dev/null +++ b/src/error/error.h @@ -0,0 +1,143 @@ +#ifndef _JH_ERROR_ERROR_H_ +#define _JH_ERROR_ERROR_H_ + +#include <stdio.h> + +#include "../pervasive.h" + +#ifndef JH_DEBUG_PROGRAM_FLOW +   #define JH_DEBUG_PROGRAM_FLOW   (0 || JH_DEBUG_ALL) +#endif + +#ifndef JH_DEBUG_CONFIG +   #define JH_DEBUG_CONFIG         (0 || JH_DEBUG_ALL) +#endif + +#ifndef JH_DEBUG_LEARNING +   #define JH_DEBUG_LEARNING       (0 || JH_DEBUG_ALL) +#endif + +#ifndef JH_DEBUG_NETWORK +   #define JH_DEBUG_NETWORK  1 +#endif + +#ifndef JH_DEBUG_NETWORK +   #define JH_DEBUG_NETWORK        (0 || JH_DEBUG_ALL) +#endif + +#define JH_ENABLE_WARNINGS_OUTPUT              1 +#define JH_ENABLE_RUNTIME_ERRORS_OUTPUT        1 +#define JH_ENABLE_PROGRAMMING_ERRORS_OUTPUT    1 +#define JH_ENABLE_FATAL_ERROR_OUTPUT           1 + +#ifdef JH_ENABLE_ERROR_LOCATION +   #define JH_LOCATION " [" __FILE__ "][" JH_TO_STRING(__LINE__) "]" +#else +   #define JH_LOCATION "" +#endif + +#define JH_PRINT_STDERR(io, symbol, str, ...)\ +   fprintf(io, "[" symbol "]" JH_LOCATION " " str "\n", __VA_ARGS__); + +/* + * Given that we use preprocessor contants as flags, we can expect the compilers + * to remove the test condition for disabled flags. No need to be shy about + * allowing many debug options. + */ + +#define JH_DEBUG(io, flag, str, ...)\ +   JH_ISOLATE\ +   (\ +      if (flag)\ +      {\ +         JH_PRINT_STDERR(io, "D", str, __VA_ARGS__);\ +      }\ +   ) + + +#define JH_WARNING(io, str, ...)\ +   JH_ISOLATE\ +   (\ +      if (JH_ENABLE_WARNINGS_OUTPUT)\ +      {\ +         JH_PRINT_STDERR(io, "W", str, __VA_ARGS__);\ +      }\ +   ) + +#define JH_ERROR(io, str, ...)\ +   JH_ISOLATE\ +   (\ +      if (JH_ENABLE_RUNTIME_ERRORS_OUTPUT)\ +      {\ +         JH_PRINT_STDERR(io, "E", str, __VA_ARGS__);\ +      }\ +   ) + +#define JH_PROG_ERROR(io, str, ...)\ +   JH_ISOLATE\ +   (\ +      if (JH_ENABLE_PROGRAMMING_ERRORS_OUTPUT)\ +      {\ +         JH_PRINT_STDERR(io, "P", str, __VA_ARGS__);\ +      }\ +   ) + +#define JH_FATAL(io, str, ...)\ +   JH_ISOLATE\ +   (\ +     if (JH_ENABLE_FATAL_ERROR_OUTPUT)\ +      {\ +         JH_PRINT_STDERR(io, "F", str, __VA_ARGS__);\ +      }\ +   ) + +/* For outputs without dynamic content (static). ******************************/ + +#define JH_PRINT_S_STDERR(io, symbol, str)\ +   fprintf(io, "[" symbol "]" JH_LOCATION " " str "\n"); + +#define JH_S_DEBUG(io, flag, str)\ +   JH_ISOLATE\ +   (\ +      if (flag)\ +      {\ +         JH_PRINT_S_STDERR(io, "D", str);\ +      }\ +   ) + +#define JH_S_WARNING(io, str)\ +   JH_ISOLATE\ +   (\ +      if (JH_ENABLE_WARNINGS_OUTPUT)\ +      {\ +         JH_PRINT_S_STDERR(io, "W", str);\ +      }\ +   ) + +#define JH_S_ERROR(io, str)\ +   JH_ISOLATE\ +   (\ +      if (JH_ENABLE_RUNTIME_ERRORS_OUTPUT)\ +      {\ +         JH_PRINT_S_STDERR(io, "E", str);\ +      }\ +   ) + +#define JH_S_PROG_ERROR(io, str)\ +   JH_ISOLATE\ +   (\ +      if (JH_ENABLE_PROGRAMMING_ERRORS_OUTPUT)\ +      {\ +         JH_PRINT_S_STDERR(io, "P", str);\ +      }\ +   ) + +#define JH_S_FATAL(io, str)\ +   JH_ISOLATE\ +   (\ +     if (JH_ENABLE_FATAL_ERROR_OUTPUT)\ +      {\ +         JH_PRINT_S_STDERR(io, "F", str);\ +      }\ +   ) +#endif diff --git a/src/irc/CMakeLists.txt b/src/irc/CMakeLists.txt new file mode 100644 index 0000000..b037d15 --- /dev/null +++ b/src/irc/CMakeLists.txt @@ -0,0 +1,11 @@ +set( +   SRC_FILES ${SRC_FILES} +   ${CMAKE_CURRENT_SOURCE_DIR}/irc.c +   ${CMAKE_CURRENT_SOURCE_DIR}/irc_handle_connected.c +   ${CMAKE_CURRENT_SOURCE_DIR}/irc_handle_dcc_events.c +   ${CMAKE_CURRENT_SOURCE_DIR}/irc_receive.c +   ${CMAKE_CURRENT_SOURCE_DIR}/irc_select.c +   ${CMAKE_CURRENT_SOURCE_DIR}/irc_send.c +) + +set(SRC_FILES ${SRC_FILES} PARENT_SCOPE) diff --git a/src/irc/irc.c b/src/irc/irc.c new file mode 100644 index 0000000..3e0bca7 --- /dev/null +++ b/src/irc/irc.c @@ -0,0 +1,110 @@ +#include <libircclient/libircclient.h> + +#include <string.h> + +#include "../error/error.h" + +#include "../parameters/parameters.h" + +#include "irc.h" +#include "irc_event_handlers.h" + +int JH_irc_initialize +( +   struct JH_irc irc [const restrict static 1], +   const struct JH_parameters params [const restrict static 1], +   struct JH_meta_net jh_net [const restrict static 1] +) +{ +   memset((void *) &(irc->callbacks), 0, sizeof(irc_callbacks_t)); + +   irc->callbacks.event_connect     = JH_irc_handle_connected_event; +   irc->callbacks.event_join        = JH_irc_do_nothing; +   irc->callbacks.event_nick        = JH_irc_do_nothing; +   irc->callbacks.event_quit        = JH_irc_do_nothing; +   irc->callbacks.event_part        = JH_irc_do_nothing; +   irc->callbacks.event_mode        = JH_irc_do_nothing; +   irc->callbacks.event_topic       = JH_irc_do_nothing; +   irc->callbacks.event_kick        = JH_irc_do_nothing; +   irc->callbacks.event_channel     = JH_irc_handle_channel_message_event; +   irc->callbacks.event_privmsg     = JH_irc_do_nothing; +   irc->callbacks.event_notice      = JH_irc_do_nothing; +   irc->callbacks.event_invite      = JH_irc_do_nothing; +   irc->callbacks.event_umode       = JH_irc_do_nothing; + +   irc->callbacks.event_ctcp_rep    = JH_irc_do_nothing; +   irc->callbacks.event_ctcp_action = JH_irc_handle_ctcp_action_event; +   irc->callbacks.event_unknown     = JH_irc_do_nothing; +   irc->callbacks.event_numeric     = JH_irc_handle_numeric_event; + +   irc->callbacks.event_dcc_chat_req = JH_irc_handle_dcc_chat_req_event; +   irc->callbacks.event_dcc_send_req = JH_irc_handle_dcc_send_req_event; + +   irc->session = irc_create_session(&(irc->callbacks)); + +   irc->params = params; +   irc->jh_net = jh_net; + +   if (!(irc->session)) +   { +      JH_S_FATAL(stderr, "Unable to create IRC session."); + +      return -1; +   } + +   return 0; +} + +int JH_irc_connect (struct JH_irc irc [const restrict static 1]) +{ +   if (JH_parameters_get_irc_is_ipv6(irc->params)) +   { +      return +         irc_connect6 +         ( +            irc->session, +            JH_parameters_get_irc_server(irc->params), +            JH_parameters_get_irc_port(irc->params), +            JH_parameters_get_irc_password(irc->params), +            JH_parameters_get_irc_nick(irc->params), +            JH_parameters_get_irc_username(irc->params), +            JH_parameters_get_irc_realname(irc->params) +         ); +   } +   else +   { +      return +         irc_connect +         ( +            irc->session, +            JH_parameters_get_irc_server(irc->params), +            JH_parameters_get_irc_port(irc->params), +            JH_parameters_get_irc_password(irc->params), +            JH_parameters_get_irc_nick(irc->params), +            JH_parameters_get_irc_username(irc->params), +            JH_parameters_get_irc_realname(irc->params) +         ); +   } +} + +void JH_irc_do_nothing +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +) +{ +} + +void JH_irc_handle_numeric_event +( +   irc_session_t * session, +   unsigned int event, +   const char * origin, +   const char ** params, +   unsigned int count +) +{ +} diff --git a/src/irc/irc.h b/src/irc/irc.h new file mode 100644 index 0000000..9bdfcef --- /dev/null +++ b/src/irc/irc.h @@ -0,0 +1,43 @@ +#ifndef _JH_IRC_IRC_H_ +#define _JH_IRC_IRC_H_ + +#include "../parameters/parameters_types.h" + +#include "../meta_net/meta_net.h" + +#include "irc_types.h" + +int JH_irc_initialize +( +   struct JH_irc irc [const restrict static 1], +   const struct JH_parameters params [const restrict static 1], +   struct JH_meta_net meta_net [const restrict static 1] +); + +int JH_irc_connect (struct JH_irc irc [const restrict static 1]); + +int JH_irc_send_message +( +   struct JH_irc irc [const restrict static 1], +   const char msg [const restrict static 1] +); + +/* TODO */ +void JH_irc_finalize (struct JH_irc irc [const restrict static 1]); + +int JH_irc_pre_select +( +   struct JH_irc irc [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1], +   int max_fd [const restrict static 1] +); + +int JH_irc_post_select +( +   struct JH_irc irc [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1] +); + +#endif diff --git a/src/irc/irc_event_handlers.h b/src/irc/irc_event_handlers.h new file mode 100644 index 0000000..447fbb6 --- /dev/null +++ b/src/irc/irc_event_handlers.h @@ -0,0 +1,69 @@ +#ifndef _JH_IRC_IRC_EVENT_HANDLERS_H_ +#define _JH_IRC_IRC_EVENT_HANDLERS_H_ + +#include <libircclient/libircclient.h> + +void JH_irc_do_nothing +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +); + +void JH_irc_handle_connected_event +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +); + +void JH_irc_handle_channel_message_event +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +); + +void JH_irc_handle_ctcp_action_event +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +); + +void JH_irc_handle_numeric_event +( +   irc_session_t * session, +   unsigned int event, +   const char * origin, +   const char ** params, +   unsigned int count +); + +void JH_irc_handle_dcc_chat_req_event +( +   irc_session_t * session, +   const char * nick, +   const char * addr, +   irc_dcc_t dccid +); + +void JH_irc_handle_dcc_send_req_event +( +   irc_session_t * session, +   const char * nick, +   const char * addr, +   const char * filename, +   unsigned long size, +   irc_dcc_t dccid +); + +#endif diff --git a/src/irc/irc_handle_connected.c b/src/irc/irc_handle_connected.c new file mode 100644 index 0000000..3d24af9 --- /dev/null +++ b/src/irc/irc_handle_connected.c @@ -0,0 +1,27 @@ +#include <libircclient/libircclient.h> + +#include "../parameters/parameters.h" + +#include "irc.h" +#include "irc_event_handlers.h" + +void JH_irc_handle_connected_event +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +) +{ +   struct JH_irc * irc; + +   irc = (struct JH_irc *) irc_get_ctx(session); + +   irc_cmd_join +   ( +      session, +      JH_parameters_get_irc_channel(irc->params), +      0 +   ); +} diff --git a/src/irc/irc_handle_dcc_events.c b/src/irc/irc_handle_dcc_events.c new file mode 100644 index 0000000..2fe6904 --- /dev/null +++ b/src/irc/irc_handle_dcc_events.c @@ -0,0 +1,27 @@ +#include <libircclient/libircclient.h> + +#include "irc_event_handlers.h" + +void JH_irc_handle_dcc_chat_req_event +( +   irc_session_t * session, +   const char * nick, +   const char * addr, +   irc_dcc_t dccid +) +{ +   irc_dcc_decline(session, dccid); +} + +void JH_irc_handle_dcc_send_req_event +( +   irc_session_t * session, +   const char * nick, +   const char * addr, +   const char * filename, +   unsigned long size, +   irc_dcc_t dccid +) +{ +   irc_dcc_decline(session, dccid); +} diff --git a/src/irc/irc_receive.c b/src/irc/irc_receive.c new file mode 100644 index 0000000..0edb324 --- /dev/null +++ b/src/irc/irc_receive.c @@ -0,0 +1,50 @@ +#include <string.h> + +#include <libircclient/libircclient.h> + +#include "../meta_net/meta_net.h" + +#include "irc.h" +#include "irc_event_handlers.h" + +void JH_irc_handle_channel_message_event +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +) +{ +   struct JH_irc * irc; + +   irc = (struct JH_irc *) irc_get_ctx(session); + +   JH_meta_net_handle_user_message +   ( +      irc->jh_net, +      params[1], +      strlen(params[1]) +   ); +} + +void JH_irc_handle_ctcp_action_event +( +   irc_session_t * session, +   const char * event, +   const char * origin, +   const char ** params, +   unsigned int count +) +{ +   struct JH_irc * irc; + +   irc = (struct JH_irc *) irc_get_ctx(session); + +   JH_meta_net_handle_user_action +   ( +      irc->jh_net, +      params[0], +      strlen(params[0]) +   ); +} diff --git a/src/irc/irc_select.c b/src/irc/irc_select.c new file mode 100644 index 0000000..fe15662 --- /dev/null +++ b/src/irc/irc_select.c @@ -0,0 +1,38 @@ +#include <libircclient/libircclient.h> + +#include "irc.h" + +int JH_irc_pre_select +( +   struct JH_irc irc [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1], +   int max_fd [const restrict static 1] +) +{ +   while (!irc_is_connected(irc->session)) +   { +      /* TODO: reconnect or fail. */ +      JH_irc_connect(irc); +      /* TODO: wait a bit... */ +   } + +   irc_add_select_descriptors(irc->session, in, out, max_fd); + +   return 0; +} + +int JH_irc_post_select +( +   struct JH_irc irc [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1] +) +{ +   if (!irc_process_select_descriptors(irc->session, in, out)) +   { +      /* TODO: reconnect or fail. */ +   } + +   return 0; +} diff --git a/src/irc/irc_send.c b/src/irc/irc_send.c new file mode 100644 index 0000000..7e474be --- /dev/null +++ b/src/irc/irc_send.c @@ -0,0 +1,38 @@ +#include <libircclient/libircclient.h> + +#include "../pervasive.h" + +#include "../parameters/parameters.h" + +#include "irc.h" + +int JH_irc_send_message +( +   struct JH_irc irc [const restrict static 1], +   const char msg [const restrict static 1] +) +{ +   if (JH_IS_PREFIX("/me ", msg)) +   { +      /* TODO: what to do in case of failure? */ +      (void) irc_cmd_me +      ( +         irc->session, +         JH_parameters_get_irc_channel(irc->params), +         (msg + 4) +      ); +   } +   else +   { +      /* TODO: what to do in case of failure? */ +      (void) irc_cmd_msg +      ( +         irc->session, +         JH_parameters_get_irc_channel(irc->params), +         msg +      ); +   } + +   return 0; +} + diff --git a/src/irc/irc_types.h b/src/irc/irc_types.h new file mode 100644 index 0000000..f846076 --- /dev/null +++ b/src/irc/irc_types.h @@ -0,0 +1,14 @@ +#ifndef _JH_IRC_IRC_TYPES_H_ +#define _JH_IRC_IRC_TYPES_H_ + +#include <libircclient/libircclient.h> + +struct JH_irc +{ +   irc_callbacks_t callbacks; +   irc_session_t * session; +   const struct JH_parameters * params; +   struct JH_meta_net * jh_net; +}; + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..509255c --- /dev/null +++ b/src/main.c @@ -0,0 +1,206 @@ +/* According to POSIX.1-2001, POSIX.1-2008 */ +#include <sys/select.h> + +#include <stdio.h> + +#include "error/error.h" + +#include "parameters/parameters.h" + +#include "irc/irc.h" + +#include "meta_net/meta_net.h" + +#include "pervasive.h" + +static void print_help (const char runnable [const restrict static 1]) +{ +   printf +   ( +      "JabberHive - IRC Gateway\n" +      "Software Version %d\n" +      "Protocol Version %d\n" +      "\nUsages:\n" +      "   GATEWAY:\t%s SOCKET_NAME IRC_SERVER IRC_NICK IRC_CHANNEL IRC_PORT" +      " [OPTIONS]\n" +      "   SHOW HELP:\tAnything else.\n" +      "\nParameters:\n" +      "   SOCKET_NAME:\tValid UNIX socket.\n" +      "   IRC_SERVER:\tAddress of the IRC server. Prefixing with '#' will" +      " enable SSL.\n" +      "   IRC_NICK:\tIRC nick to be used.\n" +      "   IRC_CHANNEL:\tIRC channel to connect to.\n" +      "   IRC_PORT:\tPort to use for the IRC connection.\n" +      "\nOptions:\n" +      "   -6, --ipv6:\tEnables IPv6.\n" +      "   -u USERNAME, --username USERNAME:\tSets the IRC username.\n" +      "   -r REALNAME, --realname REALNAME:\tSets the IRC realname.\n" +      "   -p PASSWORD, --password PASSWORD:\tSets the IRC password.\n", +      JH_PROGRAM_VERSION, +      JH_PROTOCOL_VERSION, +      runnable +   ); +} + +static int initialize +( +   struct JH_irc irc [const restrict static 1], +   struct JH_meta_net socket [const restrict static 1], +   struct JH_parameters params [const restrict static 1], +   const int argc, +   const char * argv [const static argc] +) +{ +   if (JH_parameters_initialize(params, argc, argv) < 0) +   { +      print_help(argv[0]); + +      return -1; +   } + +   JH_meta_net_initialize(socket); + +   if (JH_irc_initialize(irc, params, socket) < 0) +   { +      JH_meta_net_finalize(socket); + +      return -1; +   } + +   return 0; +} + +static int connect_all +( +   struct JH_irc irc [const restrict static 1], +   struct JH_meta_net socket [const restrict static 1], +   struct JH_parameters params [const restrict static 1] +) +{ +   if (JH_meta_net_connect(socket, params) < 0) +   { +      return -1; +   } + +   if (JH_irc_connect(irc) < 0) +   { +      return -1; +   } + +   return 0; +} + +static int event_handling_loop +( +   struct JH_irc irc [const restrict static 1], +   struct JH_meta_net socket [const restrict static 1], +   struct JH_parameters params [const restrict static 1] +) +{ +   struct timeval tv; +   fd_set in_set, out_set; +   int fd_max, error; + +   for (;;) +   { + +      tv.tv_usec = 250000; +      tv.tv_sec = 0; + +      FD_ZERO(&in_set); +      FD_ZERO(&out_set); + +      fd_max = 0; + +      if (JH_irc_pre_select(irc, &in_set, &out_set, &fd_max) < 0) +      { +         JH_meta_net_finalize(socket); + +         return -1; +      } + +      if +      ( +         JH_meta_net_pre_select +         ( +            socket, +            params, +            &in_set, +            &out_set, +            &fd_max +         ) < 0 +      ) +      { +         JH_irc_finalize(irc); + +         return -1; +      } + +      error = +         select +         ( +            (fd_max + 1), +            &in_set, +            &out_set, +            (fd_set *) NULL, +            &tv +         ); + +      if (error < 0) +      { +         JH_ERROR +         ( +            stderr, +            "Unable to select the sockets: %s.", +            strerror(error) +         ); +      } + +      if (JH_irc_post_select(irc, &in_set, &out_set) < 0) +      { +         JH_meta_net_finalize(socket); + +         return -1; +      } + +      if +      ( +         JH_meta_net_post_select +         ( +            socket, +            params, +            &in_set, +            &out_set +         ) < 0 +      ) +      { +         JH_irc_finalize(irc); + +         return -1; +      } +   } + +   return 0; +} + +int main (const int argc, const char * argv [const static argc]) +{ +   struct JH_parameters params; +   struct JH_meta_net socket; +   struct JH_irc irc; + +   if (initialize(&irc, &socket, ¶ms, argc, argv) < 0) +   { +      return -1; +   } + +   if (connect_all(&irc, &socket, ¶ms) < 0) +   { +      JH_irc_finalize(&irc); +      JH_meta_net_finalize(&socket); + +      return -1; +   } + +   return event_handling_loop(&irc, &socket, ¶ms); +} diff --git a/src/meta_net/CMakeLists.txt b/src/meta_net/CMakeLists.txt new file mode 100644 index 0000000..ca047c3 --- /dev/null +++ b/src/meta_net/CMakeLists.txt @@ -0,0 +1,9 @@ +set( +   SRC_FILES ${SRC_FILES} +   ${CMAKE_CURRENT_SOURCE_DIR}/meta_net.c +   ${CMAKE_CURRENT_SOURCE_DIR}/meta_net_handle_reply.c +   ${CMAKE_CURRENT_SOURCE_DIR}/meta_net_select.c +   ${CMAKE_CURRENT_SOURCE_DIR}/meta_net_try_request.c +) + +set(SRC_FILES ${SRC_FILES} PARENT_SCOPE) diff --git a/src/meta_net/meta_net.c b/src/meta_net/meta_net.c new file mode 100644 index 0000000..fdd0fec --- /dev/null +++ b/src/meta_net/meta_net.c @@ -0,0 +1,158 @@ +#include <sys/socket.h> +#include <sys/un.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include "../error/error.h" + +#include "../parameters/parameters.h" + +#include "meta_net.h" + +static int open_socket +( +   struct JH_meta_net s [const restrict static 1], +   const char socket_name [const restrict static 1] +) +{ +   struct sockaddr_un addr; +   int flags; +   const int old_errno = errno; + +   errno = 0; + +   s->fd = socket(AF_UNIX, SOCK_STREAM, 0); + +   if (s->fd == -1) +   { +      JH_FATAL +      ( +         stderr, +         "Unable to create socket: %s.", +         strerror(errno) +      ); + +      errno = old_errno; + +      return -1; +   } + +   errno = old_errno; + +   memset((void *) &addr, 0, sizeof(addr)); + +   addr.sun_family = AF_UNIX; + +   strncpy +   ( +      (void *) addr.sun_path, +      (const void *) socket_name, +      (sizeof(addr.sun_path) - 1) +   ); + +   errno = 0; + +   if (connect(s->fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) +   { +      JH_FATAL +      ( +         stderr, +         "Unable to connect to address: %s.", +         strerror(errno) +      ); + +      errno = old_errno; + +      close(s->fd); + +      return -1; +   } + +   errno = 0; + +   flags = fcntl(s->fd, F_GETFD, 0); + +   if (flags < 0) +   { +      JH_FATAL +      ( +         stderr, +         "Unable to get fd flag information for JabberHive socket: %s.", +         strerror(errno) +      ); + +      errno = old_errno; + +      close(s->fd); + +      return -1; +   } + +   errno = 0; + +   if (fcntl(s->fd, F_SETFL, (flags | O_NONBLOCK)) == -1) +   { +      JH_FATAL +      ( +         stderr, +         "Unable to get fd flag information for JabberHive socket: %s.", +         strerror(errno) +      ); + +      errno = old_errno; + +      close(s->fd); + +      return -1; +   } + +   errno = old_errno; + +   return 0; +} + +int JH_meta_net_connect +( +   struct JH_meta_net socket [const restrict static 1], +   const struct JH_parameters params [const restrict static 1] +) +{ +   return +      open_socket +      ( +         socket, +         JH_parameters_get_socket_name(params) +      ); +} + +void JH_meta_net_initialize +( +   struct JH_meta_net socket [const restrict static 1] +) +{ +   socket->in.data = (char *) NULL; +   socket->in.capacity = 0; +   socket->in.length = 0; +   socket->in.index = 0; + +   socket->out.data = (char *) NULL; +   socket->out.capacity = 0; +   socket->out.length = 0; +   socket->out.index = 0; + +   socket->fd = -1; +   socket->has_request_in_progress = 0; +} + +void JH_meta_net_finalize +( +   struct JH_meta_net socket [const restrict static 1] +) +{ +   /* TODO */ +} diff --git a/src/meta_net/meta_net.h b/src/meta_net/meta_net.h new file mode 100644 index 0000000..ce5c61e --- /dev/null +++ b/src/meta_net/meta_net.h @@ -0,0 +1,82 @@ +#ifndef _JH_META_NET_META_NET_H_ +#define _JH_META_NET_META_NET_H_ + +#include "../parameters/parameters_types.h" + +#include "../irc/irc_types.h" + +#include "meta_net_types.h" + +void JH_meta_net_initialize +( +   struct JH_meta_net socket [const restrict static 1] +); + +int JH_meta_net_connect +( +   struct JH_meta_net socket [const restrict static 1], +   const struct JH_parameters params [const restrict static 1] +); + +void JH_meta_net_finalize +( +   struct JH_meta_net socket [const restrict static 1] +); + +int JH_meta_net_try_request +( +   struct JH_meta_net socket [const restrict static 1], +   const char string [const restrict static 1], +   const size_t string_size +); + +int JH_meta_net_handle_user_message +( +   struct JH_meta_net socket [const restrict static 1], +   const char string [const restrict static 1], +   const size_t string_size +); + +int JH_meta_net_handle_user_action +( +   struct JH_meta_net socket [const restrict static 1], +   const char string [const restrict static 1], +   const size_t string_size +); + +int JH_meta_net_read +( +   struct JH_meta_net socket [const restrict static 1] +); + +/* TODO */ +int JH_meta_net_write +( +   struct JH_meta_net socket [const restrict static 1] +); + +void JH_meta_net_handle_reply +( +   struct JH_meta_net socket [const restrict static 1], +   struct JH_irc irc [const restrict static 1], +   const struct JH_parameters params [const restrict static 1] +); + +int JH_meta_net_pre_select +( +   struct JH_meta_net socket [const restrict static 1], +   const struct JH_parameters params [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1], +   int max_fd [const restrict static 1] +); + +int JH_meta_net_post_select +( +   struct JH_meta_net socket [const restrict static 1], +   const struct JH_parameters params [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1] +); + +#endif diff --git a/src/meta_net/meta_net_handle_reply.c b/src/meta_net/meta_net_handle_reply.c new file mode 100644 index 0000000..b823078 --- /dev/null +++ b/src/meta_net/meta_net_handle_reply.c @@ -0,0 +1,74 @@ +#include "../error/error.h" + +#include "../irc/irc.h" +#include "../parameters/parameters.h" + +#include "meta_net.h" + +void JH_meta_net_handle_reply +( +   struct JH_meta_net socket [const restrict static 1], +   struct JH_irc irc [const restrict static 1], +   const struct JH_parameters params [const restrict static 1] +) +{ +   if (socket->in.index == 0) +   { +      /* No reply to handle. */ +      return; +   } + +   socket->in.data[socket->in.index] = '\0'; + +   if (JH_IS_PREFIX("!CPV ", socket->in.data)) +   { +   } +   else if (JH_IS_PREFIX("!CPS ", socket->in.data)) +   { +   } +   else if (JH_IS_PREFIX("!GR ", socket->in.data)) +   { +      /* TODO: /me vs message should be handled prior to this. */ +      JH_irc_send_message(irc, (socket->in.data + 4)); + +      JH_DEBUG +      ( +         stderr, +         /* TODO: Parameter dependent behavior. */ +         JH_DEBUG_DISPLAY_IRC_MSG_EXCHANGES, +         "<%s> %s", +         JH_parameters_get_irc_nick(params), +         (socket->in.data + 4) +      ); +   } +   else if (JH_IS_PREFIX("!AI ", socket->in.data)) +   { +      /* TODO: Parameter dependent behavior. */ +      JH_DEBUG +      ( +         stderr, +         1, +         "Received: %s.", +         socket->in.data +      ); +   } +   else if (JH_IS_PREFIX("!P ", socket->in.data)) +   { +      socket->has_request_in_progress = 0; +   } +   else if (JH_IS_PREFIX("!N ", socket->in.data)) +   { +      JH_S_WARNING(stderr, "Received Negative reply."); + +      socket->has_request_in_progress = 0; +   } +   else +   { +      JH_WARNING +      ( +         stderr, +         "Unsupported reply received: %s.", +         socket->in.data +      ); +   } +} diff --git a/src/meta_net/meta_net_select.c b/src/meta_net/meta_net_select.c new file mode 100644 index 0000000..069a671 --- /dev/null +++ b/src/meta_net/meta_net_select.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <sys/select.h> + +#include "meta_net.h" + +int JH_meta_net_pre_select +( +   struct JH_meta_net socket [const restrict static 1], +   const struct JH_parameters params [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1], +   int max_fd [const restrict static 1] +) +{ +   FD_SET(socket->fd, in); +   FD_SET(socket->fd, out); + +   if ((*max_fd) < socket->fd) +   { +      *max_fd = socket->fd; +   } + +   return 0; +} + +int JH_meta_net_post_select +( +   struct JH_meta_net socket [const restrict static 1], +   const struct JH_parameters params [const restrict static 1], +   fd_set in [const restrict static 1], +   fd_set out [const restrict static 1] +) +{ +   if (FD_ISSET(socket->fd, in)) +   { +      if (JH_meta_net_read(socket) < 0) +      { +         /* TODO: Try to reconnect. */ +         return -1; +      } +   } + +   if (FD_ISSET(socket->fd, out)) +   { +      if (JH_meta_net_write(socket) < 0) +      { +         /* TODO: Try to reconnect. */ +         return -1; +      } +   } + +   return 0; +} diff --git a/src/meta_net/meta_net_try_request.c b/src/meta_net/meta_net_try_request.c new file mode 100644 index 0000000..121ef8d --- /dev/null +++ b/src/meta_net/meta_net_try_request.c @@ -0,0 +1,232 @@ +#include <sys/socket.h> +#include <sys/un.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdint.h> + +#include "../error/error.h" + +#include "../parameters/parameters.h" + +#include "meta_net.h" + +/******************************************************************************/ +/** MEMORY ALLOCATION *********************************************************/ +/******************************************************************************/ +static int ensure_string_capacity +( +   char * string [const restrict static 1], +   size_t string_capacity [const restrict static 1], +   const size_t string_required_capacity +) +{ +   char * new_string; + +   if (string_required_capacity <= *string_capacity) +   { +      return 0; +   } + +   new_string = +      (char *) realloc +      ( +         (void *) *string, +         ((size_t) string_required_capacity) * sizeof(char) +      ); + +   if (new_string == (char *) NULL) +   { +      JH_S_ERROR +      ( +         stderr, +         "Unable to reallocate memory to match string's required size." +      ); + +      return -1; +   } + +   *string_capacity = string_required_capacity; +   *string = new_string; + +   return 1; +} + +/******************************************************************************/ +/** EXPORTED ******************************************************************/ +/******************************************************************************/ +int JH_meta_net_handle_user_message +( +   struct JH_meta_net socket [const restrict static 1], +   const char string [const restrict static 1], +   const size_t string_size +) +{ +   if (socket->out.length != 0) +   { +      return 1; +   } + +   if +   ( +      ensure_string_capacity +      ( +         &(socket->out.data), +         &(socket->out.capacity), +         string_size +      ) +      < 0 +   ) +   { +      return -1; +   } + +   memcpy +   ( +      (void *) socket->out.data, +      (const void *) string, +      (string_size * sizeof(char)) +   ); + +   socket->out.length = string_size; +   socket->out.index = 0; + +   socket->has_request_in_progress = 1; + +   return 0; +} + +int JH_meta_net_handle_user_action +( +   struct JH_meta_net socket [const restrict static 1], +   const char string [const restrict static 1], +   const size_t string_size +) +{ +   if (socket->out.length != 0) +   { +      return 1; +   } + +   if +   ( +      ensure_string_capacity +      ( +         &(socket->out.data), +         &(socket->out.capacity), +         (string_size + (JH_META_NET_ACTION_STRING_LENGTH + 1)) +      ) +      < 0 +   ) +   { +      return -1; +   } + +   memcpy +   ( +      (void *) socket->out.data, +      (const void *) JH_META_NET_ACTION_STRING, +      (JH_META_NET_ACTION_STRING_LENGTH * sizeof(char)) +   ); + +   socket->out.data[JH_META_NET_ACTION_STRING_LENGTH] = ' '; + +   memcpy +   ( +      (void *) (socket->out.data + (JH_META_NET_ACTION_STRING_LENGTH + 1)), +      (const void *) string, +      (string_size * sizeof(char)) +   ); + +   socket->out.length = string_size; +   socket->out.index = 0; + +   socket->has_request_in_progress = 1; + +   return 0; +} + +int JH_meta_net_read +( +   struct JH_meta_net socket [const restrict static 1] +) +{ +   const int old_errno = errno; +   ssize_t in_bytes_count; + +   if ((SIZE_MAX - JH_META_NET_READ_SIZE) < socket->in.length) +   { +      JH_S_ERROR +      ( +         stderr, +         "Unable to read from JabberHive socket: max buffer size reached." +      ); + +      return -1; +   } + +   if +   ( +      ensure_string_capacity +      ( +         &(socket->in.data), +         &(socket->in.capacity), +         (socket->in.length + JH_META_NET_READ_SIZE) +      ) +      < 0 +   ) +   { +      return -1; +   } + +   errno = 0; + +   in_bytes_count = +      read +      ( +         socket->fd, +         (&(socket->in.data) + socket->in.length), +         (size_t) JH_META_NET_READ_SIZE +      ); + +   if (in_bytes_count < 0) +   { +      if (errno == EAGAIN) +      { +         errno = old_errno; + +         return 0; +      } + +      JH_ERROR +      ( +         stderr, +         "Unable to read from JabberHive socket: %s.", +         strerror(errno) +      ); + +      errno = old_errno; + +      return -1; +   } + +   errno = old_errno; + +   /* Safe. */ +   socket->in.length += (size_t) in_bytes_count; + +   while (socket->in.index < socket->in.length) +   { +      if (socket->in.data[socket->in.index] == '\n') +      { +         break; +      } + +      socket->in.index += 1; +   } + +   return 0; +} diff --git a/src/meta_net/meta_net_types.h b/src/meta_net/meta_net_types.h new file mode 100644 index 0000000..251f85b --- /dev/null +++ b/src/meta_net/meta_net_types.h @@ -0,0 +1,26 @@ +#ifndef _JH_META_NET_META_NET_TYPES_H_ +#define _JH_META_NET_META_NET_TYPES_H_ + +#include <stdio.h> + +#define JH_META_NET_READ_SIZE 64 +#define JH_META_NET_ACTION_STRING         "/me" +#define JH_META_NET_ACTION_STRING_LENGTH  3 + +struct JH_meta_net_buffer +{ +   char * data; +   size_t capacity; +   size_t length; +   size_t index; +}; + +struct JH_meta_net +{ +   int fd; +   int has_request_in_progress; +   struct JH_meta_net_buffer in; +   struct JH_meta_net_buffer out; +}; + +#endif diff --git a/src/parameters/CMakeLists.txt b/src/parameters/CMakeLists.txt new file mode 100644 index 0000000..2aa7ece --- /dev/null +++ b/src/parameters/CMakeLists.txt @@ -0,0 +1,7 @@ +set( +   SRC_FILES ${SRC_FILES} +   ${CMAKE_CURRENT_SOURCE_DIR}/parameters.c +   ${CMAKE_CURRENT_SOURCE_DIR}/parameters_getters.c +) +set(SRC_FILES ${SRC_FILES} PARENT_SCOPE) + diff --git a/src/parameters/parameters.c b/src/parameters/parameters.c new file mode 100644 index 0000000..63556f0 --- /dev/null +++ b/src/parameters/parameters.c @@ -0,0 +1,250 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> + +#include "../error/error.h" + +#include "parameters.h" + +static int parse_port +( +   struct JH_parameters param [const restrict static 1], +   const char argv [const restrict] +) +{ +   long long int input; +   const int old_errno = errno; + +   errno = 0; + +   input = strtoll(argv, (char **) NULL, 10); + +   if +   ( +      (errno != 0) +      || (input > (long long int) USHRT_MAX) +      || (input < 1) +   ) +   { +      JH_FATAL +      ( +         stderr, +         "Invalid or value for parameter 'port', accepted " +         "range is " +         "[1, %hu] (integer).", +         USHRT_MAX +      ); + +      errno = old_errno; + +      return -1; +   } + +   param->port = (unsigned short) input; + +   errno = old_errno; + +   return 0; +} + +static void set_default_to_all_fields +( +   struct JH_parameters param [const restrict static 1] +) +{ +   param->socket_name = (const char *) NULL; +   param->server = (const char *) NULL; +   param->password = (const char *) NULL; +   param->nick = (const char *) NULL; +   param->username = (const char *) NULL; +   param->realname = (const char *) NULL; +   param->channel = (const char *) NULL; +   param->port = 0; +   param->use_ipv6 = 0; +} + +static int is_valid +( +   struct JH_parameters param [const restrict static 1] +) +{ +   int valid; + +   valid = 1; + +   if (param->socket_name == (const char *) NULL) +   { +      JH_S_FATAL(stderr, "Missing parameter: Zero of One socket name."); + +      valid = 0; +   } + +   if (param->server == (const char *) NULL) +   { +      JH_S_FATAL(stderr, "Missing parameter: IRC server address."); + +      valid = 0; +   } + +   if (param->nick == (const char *) NULL) +   { +      JH_S_FATAL(stderr, "Missing parameter: IRC nick."); + +      valid = 0; +   } + +   if (param->channel == (const char *) NULL) +   { +      JH_S_FATAL(stderr, "Missing parameter: IRC channel."); + +      valid = 0; +   } + +   if (param->port == 0) +   { +      JH_S_FATAL(stderr, "Missing parameter: IRC port."); + +      valid = 0; +   } + +   return valid; +} + +static void set_parameters +( +   struct JH_parameters param [const restrict static 1], +   int const argc, +   const char * argv [const static argc] +) +{ +   if (argc < 2) +   { +      return; +   } + +   param->socket_name = argv[1]; + +   if (argc < 3) +   { +      return; +   } + +   param->server = argv[2]; + +   if (argc < 4) +   { +      return; +   } + +   param->nick = argv[3]; + +   if (argc < 5) +   { +      return; +   } + +   param->channel = argv[4]; + +   if (argc < 6) +   { +      return; +   } + +   parse_port(param, argv[5]); +} + +static int set_options +( +   struct JH_parameters param [const restrict static 1], +   int const argc, +   const char * argv [const static argc] +) +{ +   int i; + +   for (i = (JH_PARAMETERS_COUNT + 1); i < argc; ++i) +   { + +      if +      ( +         JH_STRING_EQUALS("-6", argv[i]) +         || JH_STRING_EQUALS("--ipv6", argv[i]) +      ) +      { +         param->use_ipv6 = 1; +      } +      else if +      ( +         JH_STRING_EQUALS("-u", argv[i]) +         || JH_STRING_EQUALS("--username", argv[i]) +      ) +      { +         if (i == (argc - 1)) +         { +            JH_FATAL(stderr, "Missing value for option \"%s\".", argv[i]); + +            return -1; +         } + +         param->username = argv[++i]; +      } +      else if +      ( +         JH_STRING_EQUALS("-r", argv[i]) +         || JH_STRING_EQUALS("--realname", argv[i]) +      ) +      { +         if (i == (argc - 1)) +         { +            JH_FATAL(stderr, "Missing value for option \"%s\".", argv[i]); + +            return -1; +         } + +         param->realname = argv[++i]; +      } +      else if +      ( +         JH_STRING_EQUALS("-p", argv[i]) +         || JH_STRING_EQUALS("--password", argv[i]) +      ) +      { +         if (i == (argc - 1)) +         { +            JH_FATAL(stderr, "Missing value for option \"%s\".", argv[i]); + +            return -1; +         } + +         param->password = argv[++i]; +      } +      else +      { +         JH_FATAL(stderr, "Unrecognized option \"%s\".", argv[i]); + +         return -1; +      } +   } + +   return 0; +} + +int JH_parameters_initialize +( +   struct JH_parameters param [const restrict static 1], +   int const argc, +   const char * argv [const static argc] +) +{ +   set_default_to_all_fields(param); + +   set_parameters(param, argc, argv); + +   if (!is_valid(param)) +   { +      return -1; +   } + +   return set_options(param, argc, argv); +} diff --git a/src/parameters/parameters.h b/src/parameters/parameters.h new file mode 100644 index 0000000..58fc957 --- /dev/null +++ b/src/parameters/parameters.h @@ -0,0 +1,60 @@ +#ifndef _JH_CLI_PARAMETERS_H_ +#define _JH_CLI_PARAMETERS_H_ + +#include "parameters_types.h" + +int JH_parameters_initialize +( +   struct JH_parameters param [const restrict static 1], +   int const argc, +   const char * argv [const static argc] +); + +const char * JH_parameters_get_socket_name +( +   const struct JH_parameters param [const restrict static 1] +); + +const char * JH_parameters_get_irc_server +( +   const struct JH_parameters param [const restrict static 1] +); + +unsigned short JH_parameters_get_irc_port +( +   const struct JH_parameters param [const restrict static 1] +); + +/* Having it as an invocation parameter makes it readable by other users on the + * machine. + */ +const char * JH_parameters_get_irc_password +( +   const struct JH_parameters param [const restrict static 1] +); + +const char * JH_parameters_get_irc_nick +( +   const struct JH_parameters param [const restrict static 1] +); + +const char * JH_parameters_get_irc_username +( +   const struct JH_parameters param [const restrict static 1] +); + +const char * JH_parameters_get_irc_realname +( +   const struct JH_parameters param [const restrict static 1] +); + +const char * JH_parameters_get_irc_channel +( +   const struct JH_parameters param [const restrict static 1] +); + +int JH_parameters_get_irc_is_ipv6 +( +   const struct JH_parameters param [const restrict static 1] +); +#endif diff --git a/src/parameters/parameters_getters.c b/src/parameters/parameters_getters.c new file mode 100644 index 0000000..ee2bdee --- /dev/null +++ b/src/parameters/parameters_getters.c @@ -0,0 +1,73 @@ +#include "parameters.h" + +const char * JH_parameters_get_socket_name +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->socket_name; +} + +const char * JH_parameters_get_irc_server +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->server; +} + +unsigned short JH_parameters_get_irc_port +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->port; +} + +const char * JH_parameters_get_irc_password +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->password; +} + +const char * JH_parameters_get_irc_nick +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->nick; +} + +const char * JH_parameters_get_irc_username +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->username; +} + +const char * JH_parameters_get_irc_realname +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->realname; +} + +const char * JH_parameters_get_irc_channel +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->channel; +} + +int JH_parameters_get_irc_is_ipv6 +( +   const struct JH_parameters param [const restrict static 1] +) +{ +   return param->use_ipv6; +} diff --git a/src/parameters/parameters_types.h b/src/parameters/parameters_types.h new file mode 100644 index 0000000..7d8dd1c --- /dev/null +++ b/src/parameters/parameters_types.h @@ -0,0 +1,21 @@ +#ifndef _JH_CLI_PARAMETERS_TYPES_H_ +#define _JH_CLI_PARAMETERS_TYPES_H_ + +#define JH_PARAMETERS_COUNT 5 + +struct JH_parameters +{ +   const char * restrict socket_name; + +   const char * server; +   const char * password; +   const char * nick; +   const char * username; +   const char * realname; +   const char * channel; +   unsigned short port; + +   int use_ipv6; +}; + +#endif diff --git a/src/pervasive.h b/src/pervasive.h new file mode 100644 index 0000000..e69d077 --- /dev/null +++ b/src/pervasive.h @@ -0,0 +1,31 @@ +#ifndef _JH_PERVASIVE_H_ +#define _JH_PERVASIVE_H_ + +#include <string.h> + +#define JH_PROGRAM_VERSION   1 +#define JH_PROTOCOL_VERSION  1 + +#ifdef __FRAMA_C__ +   #define JH_RUNNING_FRAMA_C   1 +#endif + +#define JH_DEBUG_ALL 1 + +#ifndef JH_DEBUG_ALL +   #define JH_DEBUG_ALL 0 +#endif +#ifndef JH_DEBUG_DISPLAY_IRC_MSG_EXCHANGES +   #define JH_DEBUG_DISPLAY_IRC_MSG_EXCHANGES JH_DEBUG_ALL +#endif + +#define JH__TO_STRING(x) #x +#define JH_TO_STRING(x) JH__TO_STRING(x) +#define JH_ISOLATE(a) do {a} while (0) + +/* strncmp stops at '\0' and strlen does not count '\0'. */ +#define JH_IS_PREFIX(a, b) (strncmp(a, b, strlen(a)) == 0) + +#define JH_STRING_EQUALS(a, b) (strcmp(a, b) == 0) + +#endif | 


