Index: ossp-pkg/l2/l2_ch_irc.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_irc.c,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_irc.c,v' 2>/dev/null --- 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 +#include +#include +#include +#include + +/* 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 };