add initial irc_conn code that can register
authorTero Marttila <terom@fixme.fi>
Sat, 28 Feb 2009 22:47:39 +0200
changeset 18 dedf137b504f
parent 17 5001564ac5fc
child 19 8c80580ccde9
add initial irc_conn code that can register
Makefile
src/irc_conn.c
src/irc_conn.h
src/irc_line.h
src/line_proto.c
src/line_proto.h
src/nexus.c
--- a/Makefile	Sat Feb 28 21:39:15 2009 +0200
+++ b/Makefile	Sat Feb 28 22:47:39 2009 +0200
@@ -34,7 +34,7 @@
 SOCK_OBJS = obj/sock.o obj/sock_tcp.o
 SOCK_GNUTLS_OBJS = obj/sock_gnutls.o
 LINEPROTO_OBJS = obj/line_proto.o
-IRC_OBJS = obj/irc_line.o
+IRC_OBJS = obj/irc_line.o obj/irc_conn.o
 
 # XXX: not yet there
 #CORE_OBJS = obj/lib/log.o obj/lib/signals.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_conn.c	Sat Feb 28 22:47:39 2009 +0200
@@ -0,0 +1,90 @@
+
+#include "irc_conn.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void irc_conn_on_line (char *line_buf, void *arg) 
+{
+    struct irc_line line;
+    int err;
+    
+    // log
+    printf("<<< %s\n", line_buf);
+
+    // parse
+    if ((err = irc_line_parse(&line, line_buf)))
+        printf("!!! Invalid line: %s: %s\n", line_buf, error_name(err));
+
+    else
+        printf("\tprefix=%s, command=%s, args={%s, %s, %s, ...}\n",
+                line.prefix, line.command, line.args[0], line.args[1], line.args[2]
+        );
+}
+
+err_t irc_conn_create (struct irc_conn **conn_ptr, struct sock_stream *sock, const struct irc_conn_config *config, struct error_info *err)
+{
+    struct irc_conn *conn;
+
+    // alloc new state struct
+    if ((conn = calloc(1, sizeof(struct irc_conn))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // create the line_proto, with our on_line handler
+    if (line_proto_create(&conn->lp, sock, IRC_LINE_MAX * 1.5, &irc_conn_on_line, conn, err))
+        return ERROR_CODE(err);
+
+    // send the initial messages
+    if (
+            irc_conn_NICK(conn, config->nickname)
+        ||  irc_conn_USER(conn, config->username, config->realname)
+    )
+        return ERROR_CODE(err);
+
+    // ok
+    *conn_ptr = conn;
+
+    return SUCCESS;
+}
+
+err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line)
+{
+    char line_buf[IRC_LINE_MAX + 2];
+    err_t err;
+
+    // format
+    if ((err = irc_line_build(line, line_buf)))
+        return err;
+    
+    // log
+    printf(">>> %s\n", line_buf);
+
+    // add CRLF
+    strcat(line_buf, "\r\n");
+
+    // send using line_proto
+    return line_proto_write(conn->lp, line_buf);
+}
+
+err_t irc_conn_NICK (struct irc_conn *conn, const char *nickname)
+{
+    // NICK <nickname>
+    struct irc_line line = {
+        NULL, "NICK", { nickname, NULL }
+    };
+    
+    // send it
+    return irc_conn_send(conn, &line);
+}
+
+err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname)
+{
+    // USER <user> <mode> <unused> <realname>
+    struct irc_line line = {
+        NULL, "USER", { username, "0", "*", realname }
+    };
+    
+    // send it
+    return irc_conn_send(conn, &line);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_conn.h	Sat Feb 28 22:47:39 2009 +0200
@@ -0,0 +1,58 @@
+#ifndef IRC_CONN_H
+#define IRC_CONN_H
+
+/*
+ * Per-connection IRC setup and state/protocol handling.
+ */
+
+#include "sock.h"
+#include "line_proto.h"
+#include "irc_line.h"
+#include "error.h"
+
+/*
+ * A connection to an IRC server.
+ */
+struct irc_conn {
+    /* We are a line-based protocol */
+    struct line_proto *lp;
+};
+
+// XXX: this should probably be slightly reworked
+struct irc_conn_config {
+    /* Nickname to use on that server */
+    const char *nickname;
+
+    /* Username to supply */
+    const char *username;
+
+    /* Realname to supply */
+    const char *realname;
+};
+
+/*
+ * Create a new irc_conn using the given sock_stream, which should be connected to an IRC server. The parameters given
+ * in \a config will be used to identify with the IRC server.
+ *
+ * On success, the resulting irc_conn is returned via *conn with SUCCESS. Otherwise, -ERR_* and error info is returned
+ * via *err.
+ */
+err_t irc_conn_create (struct irc_conn **conn, struct sock_stream *sock, const struct irc_conn_config *config, struct error_info *err);
+
+/*
+ * Send an IRC message directly
+ */
+err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line);
+
+/*
+ * Send a NICK message
+ */
+err_t irc_conn_NICK (struct irc_conn *conn, const char *nickname);
+
+/*
+ * Send a USER message
+ */
+err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname);
+
+
+#endif /* IRC_CONN_H */
--- a/src/irc_line.h	Sat Feb 28 21:39:15 2009 +0200
+++ b/src/irc_line.h	Sat Feb 28 22:47:39 2009 +0200
@@ -4,9 +4,9 @@
 #include "error.h"
 
 /*
- * The maximum length of a line, including terminating CRLF
+ * The maximum length of a line, without terminating CRLF
  */
-#define IRC_LINE_MAX 512
+#define IRC_LINE_MAX 510
 
 /*
  * The maximum number of arguments for a single command
--- a/src/line_proto.c	Sat Feb 28 21:39:15 2009 +0200
+++ b/src/line_proto.c	Sat Feb 28 22:47:39 2009 +0200
@@ -103,8 +103,11 @@
     lp->cb_arg = cb_arg;
 
     // initialize event-based stuff
-    sock_stream_event_init(sock, &line_proto_sock_stream_callbacks, lp);
-    line_proto_schedule_events(lp, EV_READ);
+    if (
+            sock_stream_event_init(sock, &line_proto_sock_stream_callbacks, lp)
+        ||  line_proto_schedule_events(lp, EV_READ)
+    )
+        return ERROR_CODE(&lp->err);
 
     // return
     *lp_ptr = lp;
@@ -214,6 +217,25 @@
     return SUCCESS;
 }
 
+int line_proto_write (struct line_proto *lp, const char *line)
+{
+    int ret;
+    size_t len = strlen(line);
+
+    // XXX: no output buffers for now :)
+    if ((ret = sock_stream_write(lp->sock, line, len)) < 0)
+        RETURN_SET_ERROR_INFO(&lp->err, sock_stream_error(lp->sock));
+
+    // EAGAIN or partial?
+    if (ret < len) {
+        // XXX: ugly hack, need partial line buffering
+        return -1;
+    }
+
+    // ok
+    return SUCCESS;
+}
+
 const struct error_info* line_proto_error (struct line_proto *lp)
 {
     // return pointer
--- a/src/line_proto.h	Sat Feb 28 21:39:15 2009 +0200
+++ b/src/line_proto.h	Sat Feb 28 22:47:39 2009 +0200
@@ -35,9 +35,12 @@
 err_t line_proto_read (struct line_proto *lp, char **line_ptr);
 
 /*
- * Signify that the line read with line_proto_read() was handled and can be discarded
+ * Write a single line to the sock_stream, buffering any incomplete fragment that remains unset. Returns zero if the
+ * line was succesfully sent, >0 if it was only partially sent, or -err on errors.
+ *
+ * The given line should already include the terminating '\r\n' character sequence.
  */
-void line_proto_discard (struct line_proto *lp);
+int line_proto_write (struct line_proto *lp, const char *line);
 
 /*
  * Get current error_info*
--- a/src/nexus.c	Sat Feb 28 21:39:15 2009 +0200
+++ b/src/nexus.c	Sat Feb 28 22:47:39 2009 +0200
@@ -8,12 +8,11 @@
 #include <event2/event.h>
 
 #include "sock.h"
-#include "line_proto.h"
-#include "irc_line.h"
+#include "irc_conn.h"
 
-#define CONNECT_HOST "irc.fixme.fi"
-#define CONNECT_SERV "6697"
-#define LINE_LENGTH 512
+#define DEFAULT_HOST "irc.fixme.fi"
+#define DEFAULT_PORT "6667"
+#define DEFAULT_PORT_SSL "6697"
 
 static struct option options[] = {
     {"help",            0,  NULL,   'h' },
@@ -33,31 +32,23 @@
     printf(" --ssl / -S             use SSL\n");
 }
 
-void on_line (char *line_buf, void *arg) 
-{
-    struct irc_line line;
-    int err;
-
-    // parse
-    if ((err = irc_line_parse(&line, line_buf)))
-        printf("!!! Invalid line: %s: %s\n", line_buf, error_name(err));
-
-    else
-        printf("<<< prefix=%s, command=%s, args={%s, %s, %s, ...}\n",
-                line.prefix, line.command, line.args[0], line.args[1], line.args[2]
-        );
-}
-
 int main (int argc, char **argv) 
 {
     int opt, option_index;
     struct event_base *ev_base;
     struct sock_stream *sock;
-    struct line_proto *lp;
+    struct irc_conn *conn;
     struct error_info _err;
 
-    const char *hostname = CONNECT_HOST, *portname = CONNECT_SERV;
+    const char *hostname = DEFAULT_HOST, *portname = DEFAULT_PORT;
     bool ssl = 0;
+    struct irc_conn_config conn_config = {
+        .nickname       = "SpBotDev",
+        .username       = "spbot-dev",
+        .realname       = "SpBot (development version)",
+    };
+
+    bool port_default = true;
     
     // parse options
     while ((opt = getopt_long(argc, argv, "hH:P:S", options, &option_index)) != -1) {
@@ -72,10 +63,15 @@
             
             case 'P':
                 portname = optarg;
+                port_default = false;
                 break;
 
             case 'S':
                 ssl = true;
+
+                if (port_default)
+                    portname = DEFAULT_PORT_SSL;
+
                 break;
 
             case '?':
@@ -103,9 +99,9 @@
 
     }
 
-    // line protocol, with safety margin for buffer
-    if (line_proto_create(&lp, sock, LINE_LENGTH * 2, on_line, NULL, &_err))
-        errx(1, "line_proto_create: %s", error_msg(&_err));
+    // create the irc connection state
+    if (irc_conn_create(&conn, sock, &conn_config, &_err))
+        errx(1, "irc_conn_create: %s", error_msg(&_err));
 
     // run event loop
     if (event_base_dispatch(ev_base))