diff options
author | stefan11111 <stefan11111@shitposting.expert> | 2024-01-14 22:07:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-14 15:07:00 -0500 |
commit | 0f70e02c1cf577aca846f1dfccabbd1bc1a4a075 (patch) | |
tree | 544c1ef6629b3cd1a50ea576746eb49a6aca2e42 | |
parent | released at 0.3.2 (#130) (diff) | |
download | kirc-0f70e02c1cf577aca846f1dfccabbd1bc1a4a075.tar.gz kirc-0f70e02c1cf577aca846f1dfccabbd1bc1a4a075.tar.bz2 kirc-0f70e02c1cf577aca846f1dfccabbd1bc1a4a075.tar.xz kirc-0f70e02c1cf577aca846f1dfccabbd1bc1a4a075.tar.zst kirc-0f70e02c1cf577aca846f1dfccabbd1bc1a4a075.zip |
Merge changes I made in the last year (#136)
Co-authored-by: Emeka Nkurumeh <emekankurumeh@outlook.com>
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | kirc.1 | 5 | ||||
-rw-r--r-- | kirc.c | 1186 | ||||
-rw-r--r-- | kirc.h | 53 |
5 files changed, 838 insertions, 424 deletions
@@ -1,24 +1,21 @@ .POSIX: ALL_WARNING = -Wall -Wextra -pedantic -ALL_LDFLAGS = $(LDFLAGS) -PREFIX = /usr/local -LDLIBS = -lm +PREFIX ?= /usr/local BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man -all: clean kirc -install: all +all: kirc.c kirc.h + $(CC) $(CFLAGS) $(LDFLAGS) kirc.c -o kirc + +install: kirc mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(MANDIR)/man1 cp -f kirc $(DESTDIR)$(BINDIR) cp -f kirc.1 $(DESTDIR)$(MANDIR)/man1 chmod 755 $(DESTDIR)$(BINDIR)/kirc chmod 644 $(DESTDIR)$(MANDIR)/man1/kirc.1 -kirc: kirc.o - $(CC) $(ALL_LDFLAGS) -o kirc kirc.o $(LDLIBS) -kirc.o: kirc.c kirc.h clean: - rm -f kirc *.o + rm -f kirc uninstall: rm -f $(DESTDIR)$(BINDIR)/kirc rm -f $(DESTDIR)$(MANDIR)/man1/kirc.1 @@ -18,6 +18,9 @@ Consult `man kirc` for a full list and explanation of available arguments. kirc [-s hostname] [-p port] [-c channels] [-n nickname] [-r realname] [-u username] [-k password] [-a token] [-o logfile] [-e|x|v|V] +## DCC + DCC transfers are always accpeted without user interaction and downloaded to the current directory. + ## Command Aliases <message> send PRIVMSG to the current channel. @@ -63,6 +63,9 @@ received. .BI /<command> Send message to IRC host (e.g. /JOIN, /PART, /WHOIS, etc.) .TP +.BI // " <text>" +Send text to current channel (useful when sending '@' and '/', usually to bots) +.TP .BI /#<channel> Set default message channel to <channel> .TP @@ -77,7 +80,7 @@ to specified or .I <nick> .TP -.BI @@<channel|nick> " <message>" +.BI /action <channel|nick> " <message>" Send CTCP ACTION containing .I <message> to specified @@ -1,19 +1,20 @@ -#include "kirc.h" - /* * SPDX-License-Identifier: MIT * Copyright (C) 2023 Michael Czigler */ +#include "kirc.h" + static void free_history(void) { - if (history) { - int j; - for (j = 0; j < history_len; j++) { - free(history[j]); - } - free(history); + if (!history) { + return; } + int j; + for (j = 0; j < history_len; j++) { + free(history[j]); + } + free(history); } static void disable_raw_mode(void) @@ -83,89 +84,68 @@ static int get_cursor_position(int ifd, int ofd) static int get_columns(int ifd, int ofd) { struct winsize ws; - if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { - int start = get_cursor_position(ifd, ofd); - if (start == -1) { - return 80; - } - if (write(ofd, "\x1b[999C", 6) != 6) { - return 80; - } - int cols = get_cursor_position(ifd, ofd); - if (cols == -1) { - return 80; - } - if (cols > start) { - char seq[32]; - snprintf(seq, sizeof(seq), "\x1b[%dD", cols - start); - if (write(ofd, seq, strnlen(seq, MSG_MAX)) == -1) { - } - } - return cols; - } else { + if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0) { return ws.ws_col; } -} - -static void -buffer_position_move(state l, ssize_t dest, ssize_t src, size_t size) -{ - memmove(l->buf + l->posb + dest, l->buf + l->posb + src, size); -} - -static void buffer_position_move_end(state l, ssize_t dest, ssize_t src) -{ - buffer_position_move(l, dest, src, l->lenb - (l->posb + src) + 1); -} - -static int u8_character_start(char c) -{ - int ret = 1; - if (isu8 != 0) { - ret = (c & 0x80) == 0x00 || (c & 0xC0) == 0xC0; + int start = get_cursor_position(ifd, ofd); + if (start == -1) { + return 80; + } + if (write(ofd, "\x1b[999C", 6) != 6) { + return 80; + } + int cols = get_cursor_position(ifd, ofd); + if (cols == -1) { + return 80; } - return ret; + if (cols <= start) { + return cols; + } + char seq[32]; + snprintf(seq, sizeof(seq), "\x1b[%dD", cols - start); + (void)write(ofd, seq, strnlen(seq, 32)); + return cols; } static int u8_character_size(char c) { - int ret = 1; - if (isu8 != 0) { - int size = 0; - while (c & (0x80 >> size)) { - size++; - } - ret = (size != 0) ? size : 1; + if (isu8 == 0){ + return 1; } - return ret; + int size = 0; + while (c & (0x80 >> size++)); + return size ? size : 1; } static size_t u8_length(const char *s) { size_t lenu8 = 0; while (*s != '\0') { - lenu8 += u8_character_start(*(s++)); + lenu8 += u8_character_start(*s); + s++; } return lenu8; } static size_t u8_previous(const char *s, size_t posb) { - if (posb != 0) { - do { - posb--; - } while ((posb > 0) && !u8_character_start(s[posb])); + if (posb == 0) { + return 0; } + do { + posb--; + } while ((posb > 0) && !u8_character_start(s[posb])); return posb; } static size_t u8_next(const char *s, size_t posb) { - if (s[posb] != '\0') { - do { - posb++; - } while ((s[posb] != '\0') && !u8_character_start(s[posb])); + if (s[posb] == '\0') { + return posb; } + do { + posb++; + } while ((s[posb] != '\0') && !u8_character_start(s[posb])); return posb; } @@ -180,40 +160,28 @@ static int setIsu8_C(int ifd, int ofd) if (get_cursor_position(ifd, ofd) != 1) { return -1; } - const char *testChars[] = { - "\xe1\xbb\xa4", - NULL - }; - for (const char **it = testChars; *it; it++) { - if (write(ofd, *it, strlen(*it)) != (ssize_t) strlen(*it)) { - return -1; - } - int pos = get_cursor_position(ifd, ofd); - if (write(ofd, "\r", 1) != 1) { - return -1; - } - for (int i = 1; i < pos; i++) { - if (write(ofd, " ", 1) != 1) { - return -1; - } - } - if (write(ofd, "\r", 1) != 1) { + if (write(ofd, TESTCHARS, sizeof(TESTCHARS) - 1) != sizeof(TESTCHARS) - 1) { + return -1; + } + int pos = get_cursor_position(ifd, ofd); + if (write(ofd, "\r", 1) != 1) { + return -1; + } + for (int i = 1; i < pos; i++) { + if (write(ofd, " ", 1) != 1) { return -1; } - if (pos != 2) { - return 0; - } + } + if (write(ofd, "\r", 1) != 1) { + return -1; + } + if (pos != 2) { + return 0; } isu8 = 1; return 0; } -static void ab_initialize(struct abuf *ab) -{ - ab->b = NULL; - ab->len = 0; -} - static void ab_append(struct abuf *ab, const char *s, int len) { char *new = realloc(ab->b, ab->len + len); @@ -225,16 +193,9 @@ static void ab_append(struct abuf *ab, const char *s, int len) ab->len += len; } -static void ab_free(struct abuf *ab) -{ - free(ab->b); -} - static void refresh_line(state l) { - char seq[64]; size_t plenu8 = l->plenu8 + 2; - int fd = STDOUT_FILENO; char *buf = l->buf; size_t lenb = l->lenb; size_t posu8 = l->posu8; @@ -245,115 +206,123 @@ static void refresh_line(state l) buf += u8_next(buf, 0); posu8--; } - while (txtlenb < lenb && ch++ < l->cols) + while (txtlenb < lenb && ch++ < l->cols) { txtlenb += u8_next(buf + txtlenb, 0); - ab_initialize(&ab); - snprintf(seq, sizeof(seq), "\r"); - ab_append(&ab, seq, strnlen(seq, MSG_MAX)); - ab_append(&ab, l->prompt, l->plenb); - ab_append(&ab, "> ", 2); + } + ab.b = NULL; + ab.len = 0; + ab_append(&ab, "\r", sizeof("\r") - 1); + ab_append(&ab, chan, l->plenb); + ab_append(&ab, "> ", sizeof("> ") - 1); ab_append(&ab, buf, txtlenb); - snprintf(seq, sizeof(seq), "\x1b[0K"); - ab_append(&ab, seq, strnlen(seq, MSG_MAX)); + ab_append(&ab, "\x1b[0K", sizeof("\x1b[0K") - 1); if (posu8 + plenu8) { + char seq[32]; snprintf(seq, sizeof(seq), "\r\x1b[%dC", (int)(posu8 + plenu8)); + ab_append(&ab, seq, strnlen(seq, 32)); } else { - snprintf(seq, sizeof(seq), "\r"); + ab_append(&ab, "\r", sizeof("\r") - 1); } - ab_append(&ab, seq, strnlen(seq, MSG_MAX)); - if (write(fd, ab.b, ab.len) == -1) { + if (write(STDOUT_FILENO, ab.b, ab.len) == -1) { } - ab_free(&ab); + free(ab.b); } static int edit_insert(state l, char *c) { size_t clenb = strlen(c); - if ((l->lenb + clenb) < l->buflen) { - if (l->lenu8 == l->posu8) { - strcpy(l->buf + l->posb, c); - l->posu8++; - l->lenu8++; - l->posb += clenb; - l->lenb += clenb; - if ((l->plenu8 + l->lenu8) < l->cols) { - if (write(STDOUT_FILENO, c, clenb) == -1) { - return -1; - } - } else { - refresh_line(l); - } - } else { - buffer_position_move_end(l, clenb, 0); - memmove(l->buf + l->posb, c, clenb); - l->posu8++; - l->lenu8++; - l->posb += clenb; - l->lenb += clenb; - refresh_line(l); - } + if ((l->lenb + clenb) >= l->buflen) { + return 0; + } + if (l->lenu8 != l->posu8) { + buffer_position_move_end(l, clenb, 0); + memmove(l->buf + l->posb, c, clenb); + l->posu8++; + l->lenu8++; + l->posb += clenb; + l->lenb += clenb; + refresh_line(l); + return 0; + } + strcpy(l->buf + l->posb, c); + l->posu8++; + l->lenu8++; + l->posb += clenb; + l->lenb += clenb; + if ((l->plenu8 + l->lenu8) >= l->cols) { + refresh_line(l); + return 0; + } + if (write(STDOUT_FILENO, c, clenb) == -1) { + return -1; } return 0; } static void edit_move_left(state l) { - if (l->posb > 0) { - l->posb = u8_previous(l->buf, l->posb); - l->posu8--; - refresh_line(l); + if (l->posb == 0){ + return; } + l->posb = u8_previous(l->buf, l->posb); + l->posu8--; + refresh_line(l); } static void edit_move_right(state l) { - if (l->posu8 != l->lenu8) { - l->posb = u8_next(l->buf, l->posb); - l->posu8++; - refresh_line(l); + if (l->posu8 == l->lenu8) { + return; } + l->posb = u8_next(l->buf, l->posb); + l->posu8++; + refresh_line(l); } static void edit_move_home(state l) { - if (l->posb != 0) { - l->posb = 0; - l->posu8 = 0; - refresh_line(l); + if (l->posb == 0) { + return; } + l->posb = 0; + l->posu8 = 0; + refresh_line(l); } static void edit_move_end(state l) { - if (l->posu8 != l->lenu8) { - l->posb = l->lenb; - l->posu8 = l->lenu8; - refresh_line(l); + if (l->posu8 == l->lenu8) { + return; } + l->posb = l->lenb; + l->posu8 = l->lenu8; + refresh_line(l); } static void edit_delete(state l) { - if ((l->lenu8 > 0) && (l->posu8 < l->lenu8)) { - size_t this_size = u8_next(l->buf, l->posb) - l->posb; - buffer_position_move_end(l, 0, this_size); - l->lenb -= this_size; - l->lenu8--; - refresh_line(l); + if ((l->lenu8 == 0) || (l->posu8 >= l->lenu8)) { + return; } + size_t this_size = u8_next(l->buf, l->posb) - l->posb; + buffer_position_move_end(l, 0, this_size); + l->lenb -= this_size; + l->lenu8--; + refresh_line(l); } static void edit_backspace(state l) { - if ((l->posu8 > 0) && (l->lenu8 > 0)) { - size_t prev_size = l->posb - u8_previous(l->buf, l->posb); - buffer_position_move_end(l, (ssize_t) - prev_size, 0); - l->posb -= prev_size; - l->lenb -= prev_size; - l->posu8--; - l->lenu8--; - refresh_line(l); + if ((l->posu8 == 0) || (l->lenu8 == 0)) { + return; } + size_t prev_size = l->posb - u8_previous(l->buf, l->posb); + buffer_position_move_end(l, (ssize_t) - prev_size, 0); + l->posb -= prev_size; + l->lenb -= prev_size; + l->posu8--; + l->lenu8--; + refresh_line(l); } static void edit_delete_previous_word(state l) @@ -378,14 +347,14 @@ static void edit_delete_previous_word(state l) refresh_line(l); } -static void edit_delete_whole_line(state l) +static inline void edit_delete_whole_line(state l) { l->buf[0] = '\0'; l->posb = l->lenb = l->posu8 = l->lenu8 = 0; refresh_line(l); } -static void edit_delete_line_to_end(state l) +static inline void edit_delete_line_to_end(state l) { l->buf[l->posb] = '\0'; l->lenb = l->posb; @@ -395,55 +364,54 @@ static void edit_delete_line_to_end(state l) static void edit_swap_character_w_previous(state l) { - if (l->posu8 > 0 && l->posu8 < l->lenu8) { - char aux[8]; - ssize_t prev_size = l->posb - u8_previous(l->buf, l->posb); - ssize_t this_size = u8_next(l->buf, l->posb) - l->posb; - memmove(aux, l->buf + l->posb, this_size); - buffer_position_move(l, -prev_size + this_size, -prev_size, prev_size); - memmove(l->buf + l->posb - prev_size, aux, this_size); - if (l->posu8 != l->lenu8 - 1) { - l->posu8++; - l->posb += this_size; - } - refresh_line(l); + if (l->posu8 == 0 || l->posu8 >= l->lenu8) { + return; + } + char aux[8]; + ssize_t prev_size = l->posb - u8_previous(l->buf, l->posb); + ssize_t this_size = u8_next(l->buf, l->posb) - l->posb; + memmove(aux, l->buf + l->posb, this_size); + buffer_position_move(l, -prev_size + this_size, -prev_size, prev_size); + memmove(l->buf + l->posb - prev_size, aux, this_size); + if (l->posu8 != l->lenu8 - 1) { + l->posu8++; + l->posb += this_size; } + refresh_line(l); } static void edit_history(state l, int dir) { - if (history_len > 1) { - free(history[history_len - (1 + l->history_index)]); - history[history_len - (1 + l->history_index)] = strdup(l->buf); - l->history_index += (dir == 1) ? 1 : -1; - if (l->history_index < 0) { - l->history_index = 0; - return; - } else if (l->history_index >= history_len) { - l->history_index = history_len - 1; - return; - } - strncpy(l->buf, history[history_len - (1 + l->history_index)], - l->buflen); - l->buf[l->buflen - 1] = '\0'; - l->lenb = l->posb = strnlen(l->buf, MSG_MAX); - l->lenu8 = l->posu8 = u8_length(l->buf); - refresh_line(l); + if (history_len <= 1) { + return; + } + free(history[history_len - (1 + l->history_index)]); + history[history_len - (1 + l->history_index)] = strdup(l->buf); + l->history_index += (dir == 1) ? 1 : -1; + if (l->history_index < 0) { + l->history_index = 0; + return; + } + if (l->history_index >= history_len) { + l->history_index = history_len - 1; + return; } + strcpy(l->buf, history[history_len - (1 + l->history_index)]); + l->buf[l->buflen - 1] = '\0'; + l->lenb = l->posb = strnlen(l->buf, MSG_MAX); + l->lenu8 = l->posu8 = u8_length(l->buf); + refresh_line(l); } static int history_add(const char *line) { char *linecopy; - if (history_max_len == 0) { - return 0; - } if (history == NULL) { - history = malloc(sizeof(char *) * history_max_len); + history = malloc(sizeof(char *) * HIS_MAX); if (history == NULL) { return 0; } - memset(history, 0, (sizeof(char *) * history_max_len)); + memset(history, 0, (sizeof(char *) * HIS_MAX)); } if (history_len && !strcmp(history[history_len - 1], line)) { return 0; @@ -452,9 +420,9 @@ static int history_add(const char *line) if (!linecopy) { return 0; } - if (history_len == history_max_len) { + if (history_len == HIS_MAX) { free(history[0]); - memmove(history, history + 1, sizeof(char *) * (history_max_len - 1)); + memmove(history, history + 1, sizeof(char *) * (HIS_MAX - 1)); history_len--; } history[history_len] = linecopy; @@ -462,7 +430,7 @@ static int history_add(const char *line) return 1; } -static void edit_enter(void) +static inline void edit_enter(void) { history_len--; free(history[history_len]); @@ -470,140 +438,143 @@ static void edit_enter(void) static void edit_escape_sequence(state l, char seq[3]) { - if (read(ttyinfd, seq, 1) == -1) + if (read(ttyinfd, seq, 1) == -1) { return; - if (read(ttyinfd, seq + 1, 1) == -1) + } + if (read(ttyinfd, seq + 1, 1) == -1) { return; - if (seq[0] == '[') { /* ESC [ sequences. */ - if ((seq[1] >= '0') && (seq[1] <= '9')) { - /* Extended escape, read additional byte. */ - if (read(ttyinfd, seq + 2, 1) == -1) { - return; - } - if ((seq[2] == '~') && (seq[1] == 3)) { - edit_delete(l); /* Delete key. */ - } - } else { - switch (seq[1]) { - case 'A': - edit_history(l, 1); - break; /* Up */ - case 'B': - edit_history(l, 0); - break; /* Down */ - case 'C': - edit_move_right(l); - break; /* Right */ - case 'D': - edit_move_left(l); - break; /* Left */ - case 'H': - edit_move_home(l); - break; /* Home */ - case 'F': - edit_move_end(l); - break; /* End */ - } + } + if (seq[0] != '[') { /* ESC [ sequences. */ + if (seq[0] != 'O') { /* ESC O sequences. */ + return; } - } else if (seq[0] == 'O') { /* ESC O sequences. */ switch (seq[1]) { case 'H': edit_move_home(l); - break; /* Home */ + return; /* Home */ case 'F': edit_move_end(l); - break; /* End */ + return; /* End */ } + return; + } + if ((seq[1] >= '0') && (seq[1] <= '9')) { + /* Extended escape, read additional byte. */ + if (read(ttyinfd, seq + 2, 1) == -1) { + return; + } + if ((seq[2] == '~') && (seq[1] == 3)) { + edit_delete(l); /* Delete key. */ + } + return; + } + switch (seq[1]) { + case 'A': + edit_history(l, 1); + return; /* Up */ + case 'B': + edit_history(l, 0); + return; /* Down */ + case 'C': + edit_move_right(l); + return; /* Right */ + case 'D': + edit_move_left(l); + return; /* Left */ + case 'H': + edit_move_home(l); + return; /* Home */ + case 'F': + edit_move_end(l); + return; /* End */ } } - static int edit(state l) { - char c, seq[3]; - int ret = 0; - ssize_t nread = read(ttyinfd, &c, 1); - if (nread <= 0) { - ret = 1; - } else { - switch (c) { - case 13: - edit_enter(); - return 1; /* enter */ - case 3: - errno = EAGAIN; - return -1; /* ctrl-c */ - case 127: /* backspace */ - case 8: - edit_backspace(l); - break; /* ctrl-h */ - case 2: - edit_move_left(l); - break; /* ctrl-b */ - case 6: - edit_move_right(l); - break; /* ctrl-f */ - case 1: - edit_move_home(l); - break; /* Ctrl+a */ - case 5: - edit_move_end(l); - break; /* ctrl+e */ - case 23: - edit_delete_previous_word(l); - break; /* ctrl+w */ - case 21: - edit_delete_whole_line(l); - break; /* Ctrl+u */ - case 11: - edit_delete_line_to_end(l); - break; /* Ctrl+k */ - case 14: - edit_history(l, 0); - break; /* Ctrl+n */ - case 16: - edit_history(l, 1); - break; /* Ctrl+p */ - case 20: - edit_swap_character_w_previous(l); - break; /* ctrl-t */ - case 27: - edit_escape_sequence(l, seq); - break; /* escape sequence */ - case 4: /* ctrl-d */ - if (l->lenu8 > 0) { - edit_delete(l); - } else { - history_len--; - free(history[history_len]); - ret = -1; - } + char c; + if (read(ttyinfd, &c, 1) <= 0) { + return 1; + } + char seq[3]; + switch (c) { + case 13: + edit_enter(); + return 1; /* enter */ + case 3: + errno = EAGAIN; + return -1; /* ctrl-c */ + case 127: /* backspace */ + case 8: + edit_backspace(l); + return 0; /* ctrl-h */ + case 2: + edit_move_left(l); + return 0; /* ctrl-b */ + case 6: + edit_move_right(l); + return 0; /* ctrl-f */ + case 1: + edit_move_home(l); + return 0; /* Ctrl+a */ + case 5: + edit_move_end(l); + return 0; /* ctrl+e */ + case 23: + edit_delete_previous_word(l); + return 0; /* ctrl+w */ + case 21: + edit_delete_whole_line(l); + return 0; /* Ctrl+u */ + case 11: + edit_delete_line_to_end(l); + return 0; /* Ctrl+k */ + case 14: + edit_history(l, 0); + return 0; /* Ctrl+n */ + case 16: + edit_history(l, 1); + return 0; /* Ctrl+p */ + case 20: + edit_swap_character_w_previous(l); + return 0; /* ctrl-t */ + case 27: + edit_escape_sequence(l, seq); + return 0; /* escape sequence */ + case 4: /* ctrl-d */ + if (l->lenu8 == 0) { + history_len--; + free(history[history_len]); + return -1; + } + edit_delete(l); + return 0; + } + if (!u8_character_start(c)) { + return 0; + } + char aux[8]; + aux[0] = c; + int size = u8_character_size(c); + for (int i = 1; i < size; i++) { + if(read(ttyinfd, aux + i, 1) == -1){ break; - default: - if (u8_character_start(c)) { - char aux[8]; - aux[0] = c; - int size = u8_character_size(c); - for (int i = 1; i < size; i++) { - nread = read(ttyinfd, aux + i, 1); - if ((aux[i] & 0xC0) != 0x80) { - break; - } - } - aux[size] = '\0'; - if (edit_insert(l, aux)) { - ret = -1; - } - } + } + if ((aux[i] & 0xC0) != 0x80) { break; } } - return ret; + aux[size] = '\0'; + if (edit_insert(l, aux)) { + return -1; + } + return 0; } -static void state_reset(state l) + +static inline void state_reset(state l) { - l->plenb = strnlen(l->prompt, MSG_MAX); - l->plenu8 = u8_length(l->prompt); + l->plenb = strnlen(chan, MSG_MAX); + l->plenu8 = u8_length(chan); l->oldposb = l->posb = l->oldposu8 = l->posu8 = l->lenb = l->lenu8 = 0; l->history_index = 0; l->buf[0] = '\0'; @@ -631,16 +602,16 @@ static void log_append(char *str, char *path) exit(1); } ctime_now(buf); - fprintf(out, "%s:", buf, str); + fprintf(out, "%s:", buf); while (*str != '\0') { if (*str >= 32 && *str < 127) { fwrite(str, sizeof(char), 1, out); } else if (*str == 3 || *str == 4) { - *str++; + str++; } else if (*str == '\n') { fwrite("\n", sizeof(char), 1, out); } - *str++; + str++; }; fclose(out); } @@ -711,9 +682,9 @@ static void message_wrap(param p) return; } char *tok; - size_t wordwidth, spacewidth = 1; size_t spaceleft = p->maxcols - (p->nicklen + p->offset); for (tok = strtok(p->message, " "); tok != NULL; tok = strtok(NULL, " ")) { + size_t wordwidth, spacewidth = 1; wordwidth = strnlen(tok, MSG_MAX); if ((wordwidth + spacewidth) > spaceleft) { printf("\r\n%*.s%s ", (int)p->nicklen + 1, " ", tok); @@ -725,7 +696,7 @@ static void message_wrap(param p) } } -static void param_print_nick(param p) +static inline void param_print_nick(param p) { printf("\x1b[35;1m%*s\x1b[0m ", p->nicklen - 4, p->nickname); printf("--> \x1b[35;1m%s\x1b[0m", p->message); @@ -734,12 +705,12 @@ static void param_print_nick(param p) static void param_print_part(param p) { printf("%*s<-- \x1b[34;1m%s\x1b[0m", p->nicklen - 3, "", p->nickname); - if (p->channel != NULL && strcmp(p->channel + 1, cdef)) { + if (p->channel != NULL && strcmp(p->channel + 1, chan)) { printf(" [\x1b[33m%s\x1b[0m] ", p->channel); } } -static void param_print_quit(param p) +static inline void param_print_quit(param p) { printf("%*s<<< \x1b[34;1m%s\x1b[0m", p->nicklen - 3, "", p->nickname); } @@ -747,41 +718,272 @@ static void param_print_quit(param p) static void param_print_join(param p) { printf("%*s--> \x1b[32;1m%s\x1b[0m", p->nicklen - 3, "", p->nickname); - if (p->channel != NULL && strcmp(p->channel + 1, cdef)) { + if (p->channel != NULL && strcmp(p->channel + 1, chan)) { printf(" [\x1b[33m%s\x1b[0m] ", p->channel); } } -static void handle_ctcp(const char *nickname, char *message) +static void print_error(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + printf("\r\x1b[31merror: "); + vprintf(fmt, ap); + printf("\x1b[0m\r\n"); + va_end(ap); +} + +static short parse_dcc_send_message(const char *message, char *filename, unsigned int *ip_addr, char *ipv6_addr, unsigned short *port, size_t *file_size) +{ + /* TODO: Fix horrible hacks */ + + char ipv6 = 0; + + if (sscanf(message, "SEND \"%" STR(FNM_MAX) "[^\"]\" %41s %hu %zu", filename, ipv6_addr, port, file_size) == 4) { + ipv6 = !!(ipv6_addr[15]); + if (ipv6 == 1) { + return 1; + } + } + if (sscanf(message, "SEND %" STR(FNM_MAX) "s %41s %hu %zu", filename, ipv6_addr, port, file_size) == 4) { + ipv6 = !!(ipv6_addr[15]); + if (ipv6 == 1) { + return 1; + } + } + if (sscanf(message, "SEND \"%" STR(FNM_MAX) "[^\"]\" %u %hu %zu", filename, ip_addr, port, file_size) == 4) { + return 0; + } + if (sscanf(message, "SEND %" STR(FNM_MAX) "s %u %hu %zu", filename, ip_addr, port, file_size) == 4) { + return 0; + } + print_error("unable to parse DCC message '%s'", message); + return -1; +} + +static short parse_dcc_accept_message(const char *message, char *filename, unsigned short *port, size_t *file_size) { - if (message[0] != '\001' && strncmp(message, "ACTION", 6)) { + if (sscanf(message, "ACCEPT \"%" STR(FNM_MAX) "[^\"]\" %hu %zu", filename, port, file_size) == 3) { + return 0; + } + if (sscanf(message, "ACCEPT %" STR(FNM_MAX) "s %hu %zu", filename, port, file_size) == 3) { + return 0; + } + print_error("unable to parse DCC message '%s'", message); + return 1; +} + +static void open_socket(int slot, int *file_fd) +{ + int sock_fd; + if(!ipv6) { + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in sockaddr = dcc_sessions.slots[slot].sin; + if (connect(sock_fd, (const struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { + close(sock_fd); + close(*file_fd); + perror("connect"); + return; + } + } + else { + sock_fd = socket(AF_INET6, SOCK_STREAM, 0); + struct sockaddr_in6 sockaddr = dcc_sessions.slots[slot].sin6; + if (connect(sock_fd, (const struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { + close(sock_fd); + close(*file_fd); + perror("connect"); + return; + } + } + if (sock_fd < 0) { + close(*file_fd); + perror("socket"); + return; + } + int flags = fcntl(sock_fd, F_GETFL, 0) | O_NONBLOCK; + if (flags < 0 || fcntl(sock_fd, F_SETFL, flags) < 0) { + close(sock_fd); + close(*file_fd); + perror("fcntl"); + return; + } + dcc_sessions.sock_fds[slot].fd = sock_fd; +} + +/* TODO: since we don't have config files how do we configure a download directory? */ +static void handle_dcc(param p) +{ + const char *message = p->message + 5; + char filename[FNM_MAX + 1]; + size_t file_size = 0; + unsigned int ip_addr = 0; + unsigned short port = 0; + char ipv6_addr[42]; + + int slot = -1; + int file_fd; + + if (!strncmp(message, "SEND", 4)) { + while(++slot < CON_MAX && dcc_sessions.slots[slot].file_fd >= 0); + + if (slot == CON_MAX) { + raw("PRIVMSG %s :XDCC CANCEL\r\n", p->nickname); + return; + } + + /* TODO: the file size parameter is optional so this isn't strictly correct. */ + + ipv6 = parse_dcc_send_message(message, filename, &ip_addr, ipv6_addr, &port, &file_size); + if(ipv6 == -1) { + return; + } + + /* TODO: at this point we should make sure that the filename is actually a filename + and not a file path. furthermore, it would be helpful to give the user + the option to rename to the file. + */ + + int file_resume = 0; + size_t bytes_read = 0; + file_fd = open(filename, DCC_FLAGS); + + if (file_fd > 0) { + struct stat statbuf = {0}; + if (fstat(file_fd, &statbuf)) { + close(file_fd); + } + bytes_read = statbuf.st_size; + file_resume = 1; + } + + if (!file_resume) { + file_fd = open(filename, DCC_FLAGS | O_CREAT, DCC_MODE); + } + + if (file_fd < 0) { + perror("open"); + exit(1); + } + + if (file_size == bytes_read) { + close(file_fd); + return; + } + + dcc_sessions.slots[slot] = (struct dcc_connection) { + .bytes_read = bytes_read, + .file_size = file_size, + .file_fd = file_fd, + }; + strcpy(dcc_sessions.slots[slot].filename, filename); + if(!ipv6) { + dcc_sessions.slots[slot].sin = (struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr = (struct in_addr){htonl(ip_addr)}, + .sin_port = htons(port), + }; + goto check_resume; + } + struct in6_addr result; + if (inet_pton(AF_INET6, ipv6_addr, &result) != 1){ + close(file_fd); + } + dcc_sessions.slots[slot].sin6 = (struct sockaddr_in6){ + .sin6_family = AF_INET6, + .sin6_addr = result, + .sin6_port = htons(port), + }; + +check_resume: + if (file_resume) { + raw("PRIVMSG %s :\001DCC RESUME \"%s\" %hu %zu\001\r\n", + p->nickname, filename, port, bytes_read); + return; + } + open_socket(slot, &file_fd); return; } - message++; + if (!strncmp(message, "ACCEPT", 6)) { + if(parse_dcc_accept_message(message, filename, &port, &file_size)) { + return; + } + + while(++slot < CON_MAX && strncmp(dcc_sessions.slots[slot].filename, filename, FNM_MAX)); + + if (slot == CON_MAX) { + return; + } + + file_fd = dcc_sessions.slots[slot].file_fd; + open_socket(slot, &file_fd); + return; + } +} + +static void handle_ctcp(param p) +{ + if (p->message[0] != '\001' && strncmp(p->message, "ACTION", 6)) { + return; + } + const char *message = p->message + 1; if (!strncmp(message, "VERSION", 7)) { - raw("NOTICE %s :\001VERSION kirc " VERSION "\001\r\n", nickname); - } else if (!strncmp(message, "TIME", 7)) { + raw("NOTICE %s :\001VERSION kirc " VERSION "\001\r\n", p->nickname); + return; + } + if (!strncmp(message, "TIME", 7)) { char buf[26]; if (!ctime_now(buf)) { - raw("NOTICE %s :\001TIME %s\001\r\n", nickname, buf); + raw("NOTICE %s :\001TIME %s\001\r\n", p->nickname, buf); } - } else if (!strncmp(message, "CLIENTINFO", 10)) { - raw("NOTICE %s :\001CLIENTINFO " CTCP_CMDS "\001\r\n", nickname); - } else if (!strncmp(message, "PING", 4)) { - raw("NOTICE %s :\001%s\r\n", nickname, message); + return; + } + if (!strncmp(message, "CLIENTINFO", 10)) { + raw("NOTICE %s :\001CLIENTINFO " CTCP_CMDS "\001\r\n", p->nickname); + return; + }if (!strncmp(message, "PING", 4)) { + raw("NOTICE %s :\001%s\r\n", p->nickname, message); + return; + } + if (!strncmp(message, "DCC", 3)) { + handle_dcc(p); + return; } } static void param_print_private(param p) { + time_t rawtime; + struct tm *timeinfo; + time ( &rawtime ); + timeinfo = localtime ( &rawtime ); + char timestamp[9]; + if (!small_screen) { + if (strnlen(p->nickname, p->nicklen) > (size_t)p->nicklen - 8) { + *(p->nickname + p->nicklen - 8) = '\0'; + } + strcpy(timestamp, "["); + char buf[5]; + if (timeinfo->tm_hour < 10) { + strcat(timestamp, "0"); + } + snprintf(buf, sizeof(buf), "%d:", timeinfo->tm_hour); + strcat(timestamp, buf); + if (timeinfo->tm_min < 10) { + strcat(timestamp, "0"); + } + snprintf(buf, sizeof(buf), "%d] ", timeinfo->tm_min); + strcat(timestamp, buf); + printf("%s", timestamp); + } int s = 0; - if (strnlen(p->nickname, MSG_MAX) <= (size_t)p->nicklen) { - s = p->nicklen - strnlen(p->nickname, MSG_MAX); + if (strnlen(p->nickname, MSG_MAX) <= (size_t)p->nicklen + strnlen(timestamp, sizeof(timestamp))) { + s = p->nicklen - strnlen(p->nickname, MSG_MAX) - strnlen(timestamp, sizeof(timestamp)); } if (p->channel != NULL && (strcmp(p->channel, nick) == 0)) { - handle_ctcp(p->nickname, p->message); - printf("%*s\x1b[33;1m%-.*s\x1b[36m ", s, "", p->nicklen, p->nickname); - } else if (p->channel != NULL && strcmp(p->channel + 1, cdef)) { + handle_ctcp(p); + printf("%*s\x1b[33;1m%-.*s [PRIVMSG]\x1b[36m ", s, "", p->nicklen, p->nickname); + } else if (p->channel != NULL && strcmp(p->channel + 1, chan)) { printf("%*s\x1b[33;1m%-.*s\x1b[0m", s, "", p->nicklen, p->nickname); printf(" [\x1b[33m%s\x1b[0m] ", p->channel); p->offset += 12 + strnlen(p->channel, CHA_MAX); @@ -825,7 +1027,6 @@ static void raw_parser(char *string) if (olog) { log_append(string, olog); } - char *tok; param_t p = { .prefix = strtok(string, " ") + 1, .suffix = strtok(NULL, ":"), @@ -835,30 +1036,48 @@ static void raw_parser(char *string) .channel = strtok(NULL, " \r"), .params = strtok(NULL, ":\r"), .maxcols = get_columns(ttyinfd, STDOUT_FILENO), - .nicklen = (p.maxcols / 3 > NIC_MAX ? NIC_MAX : p.maxcols / 3), .offset = 0 }; - if (!strncmp(p.command, "001", 3) && chan != NULL) { + if (WRAP_LEN > p.maxcols / 3) { + small_screen = 1; + p.nicklen = p.maxcols / 3; + } + else { + small_screen = 0; + p.nicklen = WRAP_LEN; + } + if (!strncmp(p.command, "001", 3) && *chan != '\0') { + char *tok; for (tok = strtok(chan, ",|"); tok != NULL; tok = strtok(NULL, ",|")) { - strcpy(cdef, tok); + strcpy(chan, tok); raw("JOIN #%s\r\n", tok); } return; - } else if (!strncmp(p.command, "QUIT", 4)) { + } + if (!strncmp(p.command, "QUIT", 4)) { param_print_quit(&p); - } else if (!strncmp(p.command, "PART", 4)) { + printf("\x1b[0m\r\n"); + return; + }if (!strncmp(p.command, "PART", 4)) { param_print_part(&p); - } else if (!strncmp(p.command, "JOIN", 4)) { + printf("\x1b[0m\r\n"); + return; + }if (!strncmp(p.command, "JOIN", 4)) { param_print_join(&p); - } else if (!strncmp(p.command, "NICK", 4)) { + printf("\x1b[0m\r\n"); + return; + }if (!strncmp(p.command, "NICK", 4)) { param_print_nick(&p); - } else if (!strncmp(p.command, "PRIVMSG", 7)) { + printf("\x1b[0m\r\n"); + return; + }if (!strncmp(p.command, "PRIVMSG", 7)) { param_print_private(&p); message_wrap(&p); - } else { - param_print_channel(&p); - message_wrap(&p); + printf("\x1b[0m\r\n"); + return; } + param_print_channel(&p); + message_wrap(&p); printf("\x1b[0m\r\n"); } @@ -873,10 +1092,9 @@ static int handle_server_message(void) if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; - } else { - perror("read"); - return -2; } + perror("read"); + return -2; } if (nread == 0) { fputs("\rconnection closed", stderr); @@ -892,8 +1110,7 @@ static int handle_server_message(void) message_buffer[i + 1] = '\0'; raw_parser(message_buffer); message_buffer[i + 1] = saved_char; - memmove(&message_buffer, &message_buffer[i + 1], - message_end - i - 1); + memmove(&message_buffer, &message_buffer[i + 1], message_end - i - 1); message_end = message_end - i - 1; i = 0; } @@ -904,9 +1121,88 @@ static int handle_server_message(void) } } +static void join_command(state l) +{ + if (!strchr(l->buf, '#')){ + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + printf("\x1b[35mIllegal channel!\x1b[0m\r\n"); + return; + } + *stpncpy(chan, strchr(l->buf, '#') + 1, MSG_MAX - 1) = '\0'; + raw("join #%s\r\n", chan); + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + printf("\x1b[35mJoined #%s!\x1b[0m\r\n", chan); +} + +static void part_command(state l) +{ + char *tok; + tok = strchr(l->buf, '#'); + if (tok){ + raw("part %s\r\n", tok); + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + printf("\x1b[35mLeft %s!\r\n", tok); + printf("\x1b[35mYou need to use /join or /# to speak in a channel!\x1b[0m\r\n"); + strcpy(chan, ""); + return; + } + tok = l->buf + 5; + while (*tok == ' ') { + tok++; + } + if (*tok == '#' || *tok == '\0') { + raw("part #%s\r\n", chan); + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + printf("\x1b[35mLeft #%s!\r\n", chan); + printf("\x1b[35mYou need to use /join or /# to speak in a channel!\x1b[0m\r\n"); + strcpy(chan, ""); + return; + } + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + printf("\x1b[35mIllegal channel!\x1b[0m\r\n"); +} + +static void msg_command(state l) +{ + char *tok; + strtok_r(l->buf + 4, " ", &tok); + int offset = 0; + while (*(l->buf + 4 + offset) == ' ') { + offset ++; + } + raw("privmsg %s :%s\r\n", l->buf + 4 + offset, tok); + if (strncmp(l->buf + 4 + offset, "NickServ", 8)) { + printf("\x1b[35mprivmsg %s :%s\x1b[0m\r\n", l->buf + 4 + offset, tok); + } +} + +static void action_command(state l) +{ + char *tok; + strtok_r(l->buf + 7, " ", &tok); + int offset = 0; + while (*(l->buf + 7 + offset) == ' ') { + offset ++; + } + raw("privmsg #%s :\001ACTION %s\001\r\n", chan, l->buf + 7 + offset); + printf("\x1b[35mprivmsg #%s :ACTION %s\x1b[0m\r\n", chan, l->buf + 7 + offset); +} + +static void nick_command(state l) +{ + char *tok; + raw("%s\r\n", l->buf + 1); + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + tok = l->buf + 5; + while (*tok == ' ') { + tok ++; + } + strcpy(nick, tok); +} + static void handle_user_input(state l) { - if (l->buf == NULL) { + if (*l->buf == '\0') { return; } char *tok; @@ -916,62 +1212,141 @@ static void handle_user_input(state l) } printf("\r\x1b[0K"); switch (l->buf[0]) { - case '/': /* send system command */ + case '/': /* send system command */ + if (!strncmp(l->buf + 1, "JOIN", 4) || !strncmp(l->buf + 1, "join", 4)) { + join_command(l); + return; + } + if (!strncmp(l->buf + 1, "PART", 4) || !strncmp(l->buf + 1, "part", 4)) { + part_command(l); + return; + } + if (l->buf[1] == '/') { + raw("privmsg #%s :%s\r\n", chan, l->buf + 2); + printf("\x1b[35mprivmsg #%s :%s\x1b[0m\r\n", chan, l->buf + 2); + return; + } + if (!strncmp(l->buf + 1, "MSG", 3) || !strncmp(l->buf + 1, "msg", 3)) { + msg_command(l); + return; + } + if (!strncmp(l->buf + 1, "NICK", 4) || !strncmp(l->buf + 1, "nick", 4)) { + nick_command(l); + return; + } + if (!strncmp(l->buf + 1, "ACTION", 6) || !strncmp(l->buf + 1, "action", 6)) { + action_command(l); + return; + } if (l->buf[1] == '#') { - strcpy(cdef, l->buf + 2); - printf("\x1b[35mnew channel: #%s\x1b[0m\r\n", cdef); - } else { - raw("%s\r\n", l->buf + 1); - printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + strcpy(chan, l->buf + 2); + printf("\x1b[35mnew channel: #%s\x1b[0m\r\n", chan); + return; } - break; - case '@': /* send private message to target channel or user */ + raw("%s\r\n", l->buf + 1); + printf("\x1b[35m%s\x1b[0m\r\n", l->buf); + return; + case '@': /* send private message to target channel or user */ strtok_r(l->buf, " ", &tok); - if (l->buf[1] == '@') { - if (l->buf[2] == '\0') { - raw("privmsg #%s :\001ACTION %s\001\r\n", cdef, tok); - printf("\x1b[35mprivmsg #%s :ACTION %s\x1b[0m\r\n", cdef, tok); - } else { - raw("privmsg %s :\001ACTION %s\001\r\n", l->buf + 2, tok); - printf("\x1b[35mprivmsg %s :ACTION %s\x1b[0m\r\n", l->buf + 2, - tok); - } - } else { + if (l->buf[1] != '@') { raw("privmsg %s :%s\r\n", l->buf + 1, tok); printf("\x1b[35mprivmsg %s :%s\x1b[0m\r\n", l->buf + 1, tok); + return; } - break; - default: /* send private message to default channel */ - raw("privmsg #%s :%s\r\n", cdef, l->buf); - printf("\x1b[35mprivmsg #%s :%s\x1b[0m\r\n", cdef, l->buf); + raw("privmsg %s :\001ACTION %s\001\r\n", l->buf + 2, tok); + printf("\x1b[35mprivmsg %s :ACTION %s\x1b[0m\r\n", l->buf + 2, tok); + return; + default: /* send private message to default channel */ + raw("privmsg #%s :%s\r\n", chan, l->buf); + printf("\x1b[35mprivmsg #%s :%s\x1b[0m\r\n", chan, l->buf); + return; } } -static void usage(void) +static inline void usage(void) { fputs("kirc [-s host] [-p port] [-c channel] [-n nick] [-r realname] \ [-u username] [-k password] [-a token] [-o path] [-e] [-x] [-v] [-V]\n", stderr); exit(2); } -static void version(void) +static inline void version(void) { fputs("kirc-" VERSION " Copyright © 2022 Michael Czigler, MIT License\n", stdout); exit(0); } -static void open_tty() -{ - if ((ttyinfd = open("/dev/tty", 0)) == -1) { - perror("failed to open /dev/tty"); - exit(1); +static inline void slot_clear(size_t i) { + memset(dcc_sessions.slots[i].filename, 0, FNM_MAX); + dcc_sessions.sock_fds[i] = (struct pollfd){.fd = -1, .events = POLLIN | POLLOUT}; + dcc_sessions.slots[i] = (struct dcc_connection){.file_fd = -1}; +} + +/* TODO: implicitly assumes that the size reported was the correct size */ +/* TODO: should we just close the file and keep on running if we get + an error we can recover from? */ +static void slot_process(state l, char *buf, size_t buf_len, size_t i) { + const char *err_str; + int sock_fd = dcc_sessions.sock_fds[i].fd; + int file_fd = dcc_sessions.slots[i].file_fd; + + if (~dcc_sessions.sock_fds[i].revents & POLLIN) { + return; + } + + int n = read(sock_fd, buf, buf_len); + if (n == -1) { + err_str = "read"; + goto handle_err; + } + + dcc_sessions.slots[i].bytes_read += n; + if (write(file_fd, buf, n) < 0) { + err_str = "write"; + goto handle_err; + } + + if (!(dcc_sessions.sock_fds[i].revents & POLLOUT)) { + refresh_line(l); + return; + } + size_t file_size = dcc_sessions.slots[i].file_size; + size_t bytes_read = dcc_sessions.slots[i].bytes_read; + unsigned ack_is_64 = file_size > UINT_MAX; + unsigned ack_shift = (1 - ack_is_64) * 32; + unsigned long long ack = htonll(bytes_read << ack_shift); + if (write(sock_fd, &ack, ack_is_64 ? 8 : 4) < 0) { + err_str = "write"; + goto handle_err; + } + if (bytes_read == file_size) { + shutdown(sock_fd, SHUT_RDWR); + close(sock_fd); + close(file_fd); + slot_clear(i); + } + refresh_line(l); + return; +handle_err: + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } + if (errno == ECONNRESET) { + shutdown(sock_fd, SHUT_RDWR); + close(sock_fd); + close(file_fd); + slot_clear(i); + return; + } else { + perror(err_str); + return; } } int main(int argc, char **argv) { - open_tty(); + char buf[BUFSIZ]; int cval; while ((cval = getopt(argc, argv, "s:p:o:n:k:c:u:r:a:exvV")) != -1) { switch (cval) { @@ -1009,7 +1384,7 @@ int main(int argc, char **argv) pass = optarg; break; case 'c': - chan = optarg; + strcpy(chan, optarg); break; case 'x': cmds = 1; @@ -1053,16 +1428,13 @@ int main(int argc, char **argv) raw("%s\r\n", inic); } } - struct pollfd fds[2] = { - {.fd = ttyinfd,.events = POLLIN}, - {.fd = conn,.events = POLLIN} - }; - char usrin[MSG_MAX]; - state_t l = { - .buf = usrin, - .buflen = MSG_MAX, - .prompt = cdef - }; + for (int i = 0; i < CON_MAX; i++) { + slot_clear(i); + } + dcc_sessions.sock_fds[CON_MAX] = (struct pollfd){.fd = ttyinfd,.events = POLLIN}; + dcc_sessions.sock_fds[CON_MAX + 1] = (struct pollfd){.fd = conn,.events = POLLIN}; + state_t l; + l.buflen = MSG_MAX; state_reset(&l); int rc, editReturnFlag = 0; if (enable_raw_mode(ttyinfd) == -1) { @@ -1072,8 +1444,8 @@ int main(int argc, char **argv) return 1; } for (;;) { - if (poll(fds, 2, -1) != -1) { - if (fds[0].revents & POLLIN) { + if (poll(dcc_sessions.sock_fds, CON_MAX + 2, -1) != -1) { + if (dcc_sessions.sock_fds[CON_MAX + 0].revents & POLLIN) { editReturnFlag = edit(&l); if (editReturnFlag > 0) { history_add(l.buf); @@ -1085,18 +1457,22 @@ int main(int argc, char **argv) } refresh_line(&l); } - if (fds[1].revents & POLLIN) { + if (dcc_sessions.sock_fds[CON_MAX + 1].revents & POLLIN) { rc = handle_server_message(); + if (rc == -2) { + return 1; + } if (rc != 0) { - if (rc == -2) { - return 1; - } return 0; } refresh_line(&l); } + + for (int i = 0; i < CON_MAX; i++) { + slot_process(&l, buf, sizeof(buf), i); + } } else { - if (errno == EAGAIN) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; } perror("poll"); @@ -1,14 +1,26 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (C) 2023 Michael Czigler + */ + #ifndef __KIRC_H #define __KIRC_H #define _POSIX_C_SOURCE 200809L -#define CTCP_CMDS "ACTION VERSION TIME CLIENTINFO PING" +#define CTCP_CMDS "ACTION VERSION TIME CLIENTINFO PING DCC" #define VERSION "0.3.2" +#define TESTCHARS "\xe1\xbb\xa4" #define MSG_MAX 512 #define CHA_MAX 200 -#define NIC_MAX 26 +#define WRAP_LEN 26 #define HIS_MAX 100 +#define FNM_MAX 255 +#define CON_MAX 20 #define CBUF_SIZ 1024 +#define DCC_FLAGS (O_WRONLY | O_APPEND) +#define DCC_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +#define STR_(a) #a +#define STR(a) STR_(a) #if defined(__FreeBSD__) || defined(__OpenBSD__) #include <sys/types.h> @@ -26,16 +38,19 @@ #include <poll.h> #include <errno.h> #include <termios.h> +#include <limits.h> #include <sys/ioctl.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> -static char cdef[MSG_MAX] = ""; /* default PRIVMSG channel */ static int conn; /* connection socket */ static int verb = 0; /* verbose output */ static int sasl = 0; /* SASL method */ static int isu8 = 0; /* UTF-8 flag */ -static char *host = "irc.libera.chat"; /* host address */ -static char *port = "6667"; /* port */ -static char *chan = NULL; /* channel(s) */ +static const char *host = "irc.libera.chat"; /* host address */ +static const char *port = "6667"; /* port */ +static char chan[MSG_MAX]; /* channel and prompt */ static char *nick = NULL; /* nickname */ static char *pass = NULL; /* password */ static char *user = NULL; /* user name */ @@ -45,14 +60,21 @@ static char *olog = NULL; /* chat log path */ static char *inic = NULL; /* additional server command */ static int cmds = 0; /* indicates additional server commands */ static char cbuf[CBUF_SIZ]; /* additional stdin server commands */ +static short ipv6 = 0; + +/* define function macros */ +#define htonll(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32))) +#define buffer_position_move(l, dest, src, size) memmove(l->buf + l->posb + dest, l->buf + l->posb + src, size) +#define buffer_position_move_end(l, dest, src) buffer_position_move(l, dest, src, l->lenb - (l->posb + src) + 1) +#define u8_character_start(c) (isu8 ? (c & 0x80) == 0x00 || (c & 0xC0) == 0xC0 : 1) static int ttyinfd = STDIN_FILENO; static struct termios orig; static int rawmode = 0; static int atexit_registered = 0; -static int history_max_len = HIS_MAX; static int history_len = 0; static char **history = NULL; +static short small_screen; typedef struct PARAMETERS { char *prefix; @@ -68,8 +90,7 @@ typedef struct PARAMETERS { } param_t, *param; typedef struct STATE { - char *prompt; /* Prompt to display. */ - char *buf; /* Edited line buffer. */ + char buf[MSG_MAX]; /* Edited line buffer. */ size_t buflen; /* Edited line buffer size. */ size_t plenb; /* Prompt length. */ size_t plenu8; /* Prompt length. */ @@ -88,4 +109,18 @@ struct abuf { int len; }; +struct dcc_connection { + char filename[FNM_MAX + 1]; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + size_t bytes_read; + size_t file_size; + int file_fd; +}; + +static struct { + struct pollfd sock_fds[CON_MAX + 2]; + struct dcc_connection slots[CON_MAX]; +} dcc_sessions = {0}; + #endif |