--- l2_ch_irc.c 2001/10/04 14:58:29 1.1
+++ l2_ch_irc.c 2001/10/19 13:06:35 1.2
@@ -24,47 +24,277 @@
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
-** l2_ch_irc.c: internet relay chat channel implementation
+** l2_ch_irc.c: Internet Relay Chat (IRC) channel implementation
*/
#include "l2.h"
+#include "l2_p.h"
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <time.h>
+
+/* declare private channel configuration */
+typedef struct {
+ char *cpLocalProg;
+ char *cpLocalHost;
+ char *cpLocalUser;
+ char *cpPassword;
+ char *cpNickname;
+ char *cpUsername;
+ char *cpRealname;
+ char *cpChannel;
+ int bJoin;
+ char *cpHost;
+ char *cpPort;
+ long nTimeout;
+ sa_addr_t *saaServer;
+ sa_t *saServer;
+} l2_ch_irc_t;
+
+/* create channel */
static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch)
{
+ l2_ch_irc_t *cfg;
+ struct utsname uts;
+ struct passwd *pw;
+
+ /* allocate private channel configuration */
+ if ((cfg = (l2_ch_irc_t *)malloc(sizeof(l2_ch_irc_t))) == NULL)
+ return L2_ERR_ARG;
+
+ /* initialize configuration with reasonable defaults */
+ cfg->cpLocalProg = NULL;
+ if ((pw = getpwuid(getuid())) != NULL)
+ cfg->cpLocalUser = strdup(pw->pw_name);
+ else
+ cfg->cpLocalUser = l2_util_asprintf("uid#%d", getuid());
+ if (uname(&uts) == 0)
+ cfg->cpLocalHost = strdup(uts.nodename);
+ else
+ cfg->cpLocalHost = strdup("localhost");
+ cfg->cpPassword = strdup("*");
+ cfg->cpNickname = strdup(cfg->cpLocalUser);
+ cfg->cpUsername = l2_util_asprintf("%s@%s", cfg->cpLocalUser, cfg->cpLocalHost);
+ cfg->cpRealname = strdup(cfg->cpUsername);
+ cfg->cpChannel = strdup("#l2");
+ cfg->bJoin = 1;
+ cfg->cpHost = NULL;
+ cfg->cpPort = strdup("6667");
+ cfg->nTimeout = 30;
+ cfg->saaServer = NULL;
+ cfg->saServer = NULL;
+
+ /* link private channel configuration into channel context */
+ ctx->vp = cfg;
+
return L2_OK;
}
+/* configure channel */
static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list ap)
{
- return L2_OK;
+ l2_ch_irc_t *cfg = (l2_ch_irc_t *)ctx->vp;
+ l2_param_t pa[12];
+ l2_result_t rv;
+
+ /* feed and call generic parameter parsing engine */
+ L2_PARAM_SET(pa[0], progname, STRING, &cfg->cpLocalProg);
+ L2_PARAM_SET(pa[1], localhost, STRING, &cfg->cpLocalHost);
+ L2_PARAM_SET(pa[2], localuser, STRING, &cfg->cpLocalUser);
+ L2_PARAM_SET(pa[3], nickname, STRING, &cfg->cpNickname);
+ L2_PARAM_SET(pa[4], username, STRING, &cfg->cpUsername);
+ L2_PARAM_SET(pa[5], realname, STRING, &cfg->cpRealname);
+ L2_PARAM_SET(pa[6], channel, STRING, &cfg->cpChannel);
+ L2_PARAM_SET(pa[7], join, INT, &cfg->bJoin);
+ L2_PARAM_SET(pa[8], host, STRING, &cfg->cpHost);
+ L2_PARAM_SET(pa[9], port, STRING, &cfg->cpPort);
+ L2_PARAM_SET(pa[10], timeout, INT, &cfg->nTimeout);
+ L2_PARAM_END(pa[11]);
+ rv = l2_util_setparams(pa, fmt, ap);
+
+ return rv;
}
+/* open channel */
static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch)
{
+ l2_ch_irc_t *cfg = (l2_ch_irc_t *)ctx->vp;
+ sa_rc_t rc;
+
+ /* make sure a path was set */
+ if (cfg->cpHost == NULL)
+ return L2_ERR_USE;
+
+ /* create socket address */
+ if ((rc = sa_addr_create(&cfg->saaServer)) != SA_OK)
+ return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
+ if ((rc = sa_addr_u2a(cfg->saaServer, "inet://%s:%s",
+ cfg->cpHost, cfg->cpPort)) != SA_OK)
+ return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
+
+ /* create socket */
+ if ((rc = sa_create(&cfg->saServer)) != SA_OK)
+ return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
+
+ /* configure socket parameters */
+ sa_timeout(cfg->saServer, SA_TIMEOUT_ALL, cfg->nTimeout, 0);
+ sa_timeout(cfg->saServer, SA_TIMEOUT_READ, 2, 0);
+ sa_buffer(cfg->saServer, SA_BUFFER_READ, 4096);
+ sa_buffer(cfg->saServer, SA_BUFFER_WRITE, 4096);
+
return L2_OK;
}
-static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch, l2_level_t level, const char *buf, size_t buf_size)
+/* write to channel */
+static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch,
+ l2_level_t level, const char *buf, size_t buf_size)
{
+ l2_ch_irc_t *cfg = (l2_ch_irc_t *)ctx->vp;
+ char caLine[1024];
+ sa_t *sa;
+ sa_addr_t *saa;
+ sa_rc_t sa_rv;
+ size_t n;
+ struct tm *tm;
+ time_t t;
+ char caDate[80];
+
+ /*
+ * Sample IRC client transaction for reference:
+ * | PASS secret
+ * | NICK rse
+ * | USER rse@engelschall.com 0 * :Ralf S. Engelschall
+ * | :irc.engelschall.com 001 rse :Welcome to the Internet Relay Network rse!~rse@dev10.dev.de.cw.net
+ * | :irc.engelschall.com 002 rse :Your host is irc.engelschall.com, running version 2.10.3p2
+ * | :irc.engelschall.com 003 rse :This server was created Wed Oct 17 2001 at 18:06:57 CEST
+ * | :irc.engelschall.com 004 rse irc.engelschall.com 2.10.3p2 aoOirwabeiIklmnoOpqrstv
+ * | :irc.engelschall.com 251 rse :There are 1 users and 0 services on 1 servers
+ * | :irc.engelschall.com 254 rse 11 :channels formed
+ * | :irc.engelschall.com 255 rse :I have 1 users, 0 services and 0 servers
+ * | :irc.engelschall.com 375 rse :- irc.engelschall.com Message of the Day -
+ * | :irc.engelschall.com 372 rse :- 17/10/2001 19:32
+ * | :irc.engelschall.com 372 rse :- Welcome to the Engelschall VISP IRC Service.
+ * | :irc.engelschall.com 376 rse :End of MOTD command.
+ * | :rse MODE rse :+i
+ * | JOIN #dev
+ * | :rse!~rse@dev10.dev.de.cw.net JOIN :#dev
+ * | :irc.engelschall.com 353 rse = #dev :@rse
+ * | :irc.engelschall.com 366 rse #dev :End of NAMES list.
+ * | PRIVMSG #dev :Sample log message line 1
+ * | PRIVMSG #dev :Sample log message line 2
+ * | PRIVMSG #dev :Sample log message line 3
+ * | QUIT
+ * | ERROR :Closing Link: rse[~rse@dev10.dev.de.cw.net] (I Quit)
+ *
+ * For more details read:
+ * RFC 2812: Internet Relay Chat: Client Protocol; C. Kalt; April 2000.
+ */
+
+ /* establish connection to server */
+ saa = cfg->saaServer;
+ sa = cfg->saServer;
+ if ((sa_rv = sa_connect(sa, saa)) != SA_OK)
+ return (sa_rv == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
+
+ /* determine current time */
+ t = time(NULL);
+ tm = localtime(&t);
+ strftime(caDate, sizeof(caDate), "%a, %d %b %Y %H:%M:%S %Z", tm);
+
+ /* write transaction to IRC server */
+ sa_writef(sa, "PASS %s\r\n", cfg->cpPassword);
+ sa_writef(sa, "NICK %s\r\n", cfg->cpNickname);
+ sa_writef(sa, "USER %s 0 * :%s\r\n", cfg->cpUsername, cfg->cpRealname);
+ if (cfg->bJoin)
+ sa_writef(sa, "JOIN %s\r\n", cfg->cpChannel);
+ sa_writef(sa, "PRIVMSG %s :", cfg->cpChannel);
+ if (cfg->cpLocalProg != NULL)
+ sa_writef(sa, "Program %s of user %s on host %s logged at %s:\r\n",
+ cfg->cpLocalProg, cfg->cpLocalUser, cfg->cpLocalHost, caDate);
+ else
+ sa_writef(sa, "A program of user %s on host %s logged at %s:\r\n",
+ cfg->cpLocalUser, cfg->cpLocalHost, caDate);
+ sa_writef(sa, "PRIVMSG %s :", cfg->cpChannel);
+ sa_write(sa, buf, buf_size-1, NULL);
+ sa_writef(sa, "\r\n");
+ if (cfg->bJoin)
+ sa_writef(sa, "PART %s\r\n", cfg->cpChannel);
+ sa_writef(sa, "QUIT\r\n");
+ sa_flush(sa);
+
+ /* shutdown write side of connection to server */
+ sa_shutdown(sa, "w");
+
+ /* still read server responses */
+ while (sa_readln(sa, caLine, sizeof(caLine), &n) == SA_OK)
+ ;
+
+ /* shutdown read side of connection to server */
+ sa_shutdown(sa, "r");
+
return L2_OK;
}
+/* close channel */
static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch)
{
+ l2_ch_irc_t *cfg = (l2_ch_irc_t *)ctx->vp;
+
+ /* destroy remote address */
+ if (cfg->saServer != NULL) {
+ sa_destroy(cfg->saServer);
+ cfg->saServer = NULL;
+ }
+ if (cfg->saaServer != NULL) {
+ sa_addr_destroy(cfg->saaServer);
+ cfg->saaServer = NULL;
+ }
+
return L2_OK;
}
+/* destroy channel */
static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch)
{
+ l2_ch_irc_t *cfg = (l2_ch_irc_t *)ctx->vp;
+
+ /* destroy channel configuration */
+ if (cfg->cpLocalProg != NULL)
+ free(cfg->cpLocalProg);
+ if (cfg->cpLocalHost != NULL)
+ free(cfg->cpLocalHost);
+ if (cfg->cpLocalUser != NULL)
+ free(cfg->cpLocalUser);
+ if (cfg->cpPassword != NULL)
+ free(cfg->cpPassword);
+ if (cfg->cpNickname != NULL)
+ free(cfg->cpNickname);
+ if (cfg->cpUsername != NULL)
+ free(cfg->cpUsername);
+ if (cfg->cpRealname != NULL)
+ free(cfg->cpRealname);
+ if (cfg->cpChannel != NULL)
+ free(cfg->cpChannel);
+ if (cfg->cpHost != NULL)
+ free(cfg->cpHost);
+ if (cfg->cpPort != NULL)
+ free(cfg->cpPort);
+ free(cfg);
+
return L2_OK;
}
+/* exported channel handler structure */
l2_handler_t l2_handler_irc = {
L2_CHANNEL_OUTPUT,
hook_create,
hook_configure,
hook_open,
hook_write,
+ NULL,
hook_close,
hook_destroy
};
|