#include <string.h>
#include <time.h>
+#include <errno.h>
+#include <wordexp.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
#include "util.h"
#include "conf.h"
#include "chat.h"
#include "view.h"
/* Global data */
-log_t *chat_log;
-int chat_len;
+server_t *servers;
+channel_t *channels;
+user_t *users;
+message_t *messages;
+int history;
/* Local data */
-static buf_t log_buf;
+static char *log_dir;
+static buf_t msg_buf;
+
+static const char *proto_map[] = {
+ [IRC] "irc",
+ [XMPP] "xmpp",
+};
/* Local functions */
+void strdcat(char *dst, const char *src, size_t n)
+{
+ int di = 0, si = 0;
+ while (di < (n-1) && dst[di])
+ di++;
+ while (di < (n-1) && src[si]) {
+ if (src[si] == '/')
+ dst[di++] = (si++,'_');
+ else
+ dst[di++] = src[si++];
+ }
+ dst[di] = '\0';
+}
+
+static void init_logs()
+{
+ if (!log_dir)
+ return;
+
+ wordexp_t wexp;
+ wordexp(log_dir, &wexp, WRDE_NOCMD);
+ if (wexp.we_wordc > 1)
+ error("Found multiple log dirs: %s\n %s\n %s\n ...",
+ log_dir, wexp.we_wordv[0], wexp.we_wordv[1]);
+ strset(&log_dir, wexp.we_wordv[0]);
+ if (mkdir(log_dir, 0755) && (errno != EEXIST))
+ error("Failed to create log dir: %s", log_dir);
+ wordfree(&wexp);
+}
+
+static void write_log(message_t *msg)
+{
+ static char log_path[4096];
-/* View init */
+ if (!log_dir)
+ return;
+
+ channel_t *chan = msg->channel;
+ server_t *srv = chan->server;
+
+ if (!chan->log) {
+ /* Create server directory */
+ strncpy(log_path, log_dir, sizeof(log_path));
+ strncat(log_path, "/", sizeof(log_path));
+ strdcat(log_path, srv->name, sizeof(log_path));
+ if (!mkdir(log_path, 0755)) {
+ debug("Failed to create server log dir\n");
+ return;
+ }
+
+ /* Open log file directory */
+ strncat(log_path, "/", sizeof(log_path));
+ strdcat(log_path, chan->name, sizeof(log_path));
+ strncat(log_path, ".log", sizeof(log_path));
+ if (!(chan->log = fopen(log_path, "a+"))) {
+ debug("Cannot open log file: %s\n", log_path);
+ return;
+ }
+ }
+
+ time_t timep = msg->when;
+ struct tm *tm = gmtime(&timep);
+
+ fprintf(chan->log, "(%04d-%02d-%02d %02d:%02d:%02d) %s%s %s\n",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ msg->from ? msg->from->name : "***",
+ msg->from ? ":" : "",
+ msg->text);
+ fflush(chan->log);
+}
+
+/* Chat init */
void chat_init(void)
{
- chat_log = (log_t*)log_buf.data;
- chat_len = 0;
+ messages = (message_t*)msg_buf.data;
+ history = 0;
+ init_logs();
irc_init();
xmpp_init();
}
+void proto_config(const char *pname, const char *sname, const char *cname,
+ const char *group, const char *name,
+ const char *key, const char *value)
+{
+ protocol_t proto = -1;
+ server_t *srv = NULL;
+ channel_t *chan = NULL;
+
+ for (srv = servers; srv; srv = srv->next)
+ if (match(sname, srv->name))
+ break;
+ for (chan = channels; chan; chan = chan->next)
+ if (match(cname, chan->name))
+ break;
+
+ if (pname) proto = get_map(pname, proto_map);
+ else if (srv) proto = srv->protocol;
+ else if (chan) proto = chan->server->protocol;
+
+ debug("chat_config: [%s-%s \"%s\"] %s = %s",
+ proto>=0 ? proto_map[proto] : "none",
+ group, name, key, value);
+
+ switch (proto) {
+ case IRC:
+ irc_config(srv, chan, group, name, key, value);
+ break;
+ case XMPP:
+ xmpp_config(srv, chan, group, name, key, value);
+ break;
+ }
+}
+
void chat_config(const char *group, const char *name, const char *key, const char *value)
{
- irc_config(group, name, key, value);
- xmpp_config(group, name, key, value);
+ if (match(group, "general")) {
+ if (match(key, "logdir"))
+ log_dir = strcopy(get_string(value));
+ }
+ if (match(group, "server")) {
+ if (match(key, ""))
+ get_name(name);
+ else if (match(key, "protocol"))
+ proto_config(value, NULL, NULL, group, name, key, value);
+ else
+ proto_config(NULL, name, NULL, group, name, key, value);
+ }
+ if (match(group, "channel")) {
+ if (match(key, ""))
+ get_name(name);
+ else if (match(key, "server"))
+ proto_config(NULL, value, NULL, group, name, key, value);
+ else
+ proto_config(NULL, NULL, name, group, name, key, value);
+ }
+ if (match(group, "autojoin")) {
+ if (match(key, ""))
+ get_name(name);
+ else
+ proto_config(NULL, name, key, group, name, key, value);
+ }
}
-void chat_notice(const char *channel, const char *from, const char *fmt, ...)
+void chat_recv(channel_t *channel, user_t *from, const char *text)
{
- static char buf[1024];
+ append(&msg_buf, NULL, sizeof(message_t));
+ messages = (message_t*)msg_buf.data;
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
+ message_t *msg = &messages[history];
+ msg->channel = channel;
+ msg->when = time(NULL);
+ msg->from = from;
+ msg->text = strcopy(text);
- chat_recv(channel, from, buf);
+ history++;
+ write_log(msg);
+ view_draw();
}
-void chat_recv(const char *channel, const char *from, const char *msg)
+void chat_complete(channel_t *channel, const char *text)
{
- append(&log_buf, NULL, sizeof(log_t));
- chat_log = (log_t*)log_buf.data;
-
- log_t *log = &chat_log[chat_len];
- log->when = time(NULL);
- log->from = strcopy(from);
- log->channel = strcopy(channel);
- log->msg = strcopy(msg);
+ switch (channel->server->protocol) {
+ case IRC:
+ irc_complete(channel, text);
+ break;
+ case XMPP:
+ xmpp_complete(channel, text);
+ break;
+ }
+}
- chat_len++;
- view_draw();
+void chat_send(channel_t *channel, const char *text)
+{
+ switch (channel->server->protocol) {
+ case IRC:
+ irc_send(channel, text);
+ break;
+ case XMPP:
+ xmpp_send(channel, text);
+ break;
+ }
}
-void chat_send(const char *channel, const char *msg)
+void chat_update(void)
{
- append(&log_buf, NULL, sizeof(log_t));
- chat_log = (log_t*)log_buf.data;
-
- log_t *log = &chat_log[chat_len];
- log->when = time(NULL);
- log->from = "andy";
- log->channel = strcopy(channel);
- log->msg = strcopy(msg);
-
- chat_len++;
- irc_send(channel, msg);
- xmpp_send(channel, msg);
view_draw();
}
irc_exit();
xmpp_exit();
- for (int i = 0; i < chat_len; i++)
- free((void*)chat_log[i].msg);
- release(&log_buf);
+ for (int i = 0; i < history; i++)
+ free((void*)messages[i].text);
+ release(&msg_buf);
+}
+
+/* Server and channel function */
+void add_server(server_t *server)
+{
+ protocol_t proto = server->protocol;
+ debug("adding %s server \"%s\"", proto_map[proto], server->name);
+ server_t **last = &servers;
+ while (*last)
+ last = &(*last)->next;
+ *last = server;
+}
+
+void add_channel(channel_t *channel)
+{
+ debug("adding %s channel \"%s\"", channel->server->name, channel->name);
+ channel_t **last = &channels;
+ while (*last)
+ last = &(*last)->next;
+ *last = channel;
+}
+
+void add_user(user_t *user)
+{
+ debug("adding %s user \"%s\"", user->server->name, user->name);
+ user_t **last = &users;
+ while (*last)
+ last = &(*last)->next;
+ *last = user;
}