This repository has been archived on 2021-04-17. You can view files and clone it, but cannot push or open issues or pull requests.
uIRC/src/uirc.c

293 lines
8.1 KiB
C
Raw Normal View History

2020-06-22 16:58:10 +00:00
/*
* This file is part of uIRC. (https://git.redxen.eu/caskd/uIRC)
* Copyright (c) 2019, 2020 Alex-David Denes
*
* uIRC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* uIRC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with uIRC. If not, see <https://www.gnu.org/licenses/>.
*/
#include "uirc.h"
const char* const uirc_ircmd[] = {
2020-06-22 16:58:10 +00:00
[PASS] = "PASS",
[NICK] = "NICK",
[USER] = "USER",
[SERVER] = "SERVER",
[OPER] = "OPER",
[SERVICE] = "SERVICE",
2020-06-22 16:58:10 +00:00
[QUIT] = "QUIT",
[SQUIT] = "SQUIT",
[JOIN] = "JOIN",
[PART] = "PART",
[MODE] = "MODE",
[TOPIC] = "TOPIC",
[NAMES] = "NAMES",
[LIST] = "LIST",
[INVITE] = "INVITE",
[KICK] = "KICK",
[VERSION] = "VERSION",
[STATS] = "STATS",
[LINKS] = "LINKS",
[TIME] = "TIME",
[CONNECT] = "CONNECT",
[TRACE] = "TRACE",
[ADMIN] = "ADMIN",
[INFO] = "INFO",
[SERVLIST] = "SERVLIST",
[SQUERY] = "SQUERY",
2020-06-22 16:58:10 +00:00
[PRIVMSG] = "PRIVMSG",
[NOTICE] = "NOTICE",
[MOTD] = "MOTD",
[LUSERS] = "LUSERS",
2020-06-22 16:58:10 +00:00
[WHO] = "WHO",
[WHOIS] = "WHOIS",
[WHOWAS] = "WHOWAS",
[KILL] = "KILL",
[PING] = "PING",
[PONG] = "PONG",
[ERROR] = "ERROR",
[AWAY] = "AWAY",
[REHASH] = "REHASH",
[DIE] = "DIE",
2020-06-22 16:58:10 +00:00
[RESTART] = "RESTART",
[SUMMON] = "SUMMON",
[USERS] = "USERS",
[WALLOPS] = "WALLOPS",
[USERHOST] = "USERHOST",
[ISON] = "ISON"};
2020-06-27 12:30:15 +00:00
struct tagmapping {
const char* name;
2020-06-27 12:30:15 +00:00
IRC_Tag* assg;
};
int uirc_tokenize_message(IRC_Message* irc_msg, char** line)
2020-06-22 16:58:10 +00:00
{
if (*line == NULL || irc_msg == NULL)
return ERR_UIRC_NULL_ARGS;
int ret;
char *progr = *line, *command;
2020-06-22 16:59:23 +00:00
if (*progr == '@') {
char* tags;
if ((tags = strtok_r(progr, " ", &progr)) != NULL) {
if ((ret = tok_tags(tags, irc_msg)) < 0)
return ret;
2020-06-22 16:59:23 +00:00
} else
return ERR_UIRC_INVALID_FORMAT;
2020-06-22 16:59:23 +00:00
}
2020-06-22 16:58:10 +00:00
if (*progr == ':') {
char* prefix;
if ((prefix = strtok_r(progr, " ", &progr)) != NULL) {
if ((ret = tok_prefix(prefix, &irc_msg->name)) < 0)
return ret;
2020-06-22 16:59:23 +00:00
} else
return ERR_UIRC_INVALID_FORMAT;
2020-06-22 16:58:10 +00:00
}
if ((command = strtok_r(NULL, " ", &progr)) != NULL) {
if (isalpha(*command)) {
if (!(irc_msg->cmd = uirc_ircmd_stoi(command)))
return ERR_UIRC_UNKNOWN_TOKEN;
} else if (isdigit(*command)) {
if ((irc_msg->cmd = atoi(command)) == 0)
return ERR_UIRC_UNKNOWN_TOKEN;
} else {
return ERR_UIRC_UNKNOWN_TOKEN;
}
2020-06-22 16:58:10 +00:00
} else {
return ERR_UIRC_INVALID_FORMAT;
2020-06-22 16:58:10 +00:00
}
int i;
for (i = 0; *progr != ':' && i < 14; i++) {
if ((irc_msg->args[i] = strtok_r(NULL, " ", &progr)) == NULL)
return ERR_UIRC_INVALID_FORMAT;
2020-06-22 16:58:10 +00:00
}
irc_msg->args[i] = NULL;
2020-06-22 16:58:10 +00:00
if (*progr == ':')
++progr;
irc_msg->trailing = progr;
2020-06-22 16:58:10 +00:00
return 1;
}
unsigned int uirc_ircmd_stoi(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
2020-06-22 16:58:10 +00:00
for (unsigned short i = PASS; i <= ISON; i++) {
if ((uirc_ircmd[i] != NULL) && (strcmp(uirc_ircmd[i], str) == 0))
2020-06-22 16:58:10 +00:00
return i;
}
return ERR_UIRC_UNKNOWN_TOKEN;
2020-06-22 16:58:10 +00:00
}
int uirc_assm_mesg(char* buf, IRC_Message* mesg)
2020-06-22 16:58:10 +00:00
{
if (buf == NULL || mesg == NULL)
return ERR_UIRC_BUFFER_ERR;
char* pos = buf;
int cnt, ret;
if ((ret = assm_tags(&pos, mesg)) < 0)
return ret;
if (mesg->name.nick != NULL || mesg->name.host != NULL) {
if ((ret = assm_prefix(&pos, &mesg->name)) < 0)
return ret;
}
if ((uirc_ircmd[mesg->cmd] != NULL) && ((cnt = sprintf(pos, "%s", uirc_ircmd[mesg->cmd])) > 0))
pos += cnt;
else
return ERR_UIRC_GENERIC;
for (unsigned int i = 0; i < 14 && mesg->args[i] != NULL; i++) {
if ((cnt = sprintf(pos, " %s", mesg->args[i])) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
}
if (mesg->trailing != NULL) {
if ((cnt = sprintf(pos, " :%s", mesg->trailing)) > 0)
pos += cnt;
else
return ERR_UIRC_GENERIC;
}
if ((cnt = sprintf(pos, "\r\n")) != 2)
return ERR_UIRC_BUFFER_ERR;
return 1;
2020-06-22 16:58:10 +00:00
}
int assm_tags(char** pos, IRC_Message* mesg)
{
if (*pos == NULL || mesg == NULL)
return ERR_UIRC_NULL_ARGS;
const char* origpos = *pos;
char* cpos = *pos;
int cnt;
const struct tagmapping tags[] = {
{.name = "time", .assg = &mesg->tags.time},
{.name = "account", .assg = &mesg->tags.account},
{.name = "batch", .assg = &mesg->tags.batch},
{.name = "label", .assg = &mesg->tags.label},
{.name = "msgid", .assg = &mesg->tags.msgid},
{.name = "multiline-concat", .assg = &mesg->tags.multiline_concat},
{.name = "typing", .assg = &mesg->tags.typing},
{.name = "react", .assg = &mesg->tags.react},
{.name = "reply", .assg = &mesg->tags.reply}};
for (unsigned int i = 0; i < sizeof(tags) / sizeof(struct tagmapping); i++) {
if ((*tags[i].assg).present) {
if (cpos == origpos) {
if (sprintf(cpos++, "@") != 1)
return ERR_UIRC_BUFFER_ERR;
} else {
if (sprintf(cpos++, ";") != 1)
return ERR_UIRC_BUFFER_ERR;
}
if ((*tags[i].assg).clientbound) {
if (sprintf(cpos++, "+") != 1)
return ERR_UIRC_BUFFER_ERR;
}
if ((*tags[i].assg).value != NULL)
cnt = sprintf(cpos, "%s=%s", tags[i].name, (*tags[i].assg).value);
else
cnt = sprintf(cpos, "%s", tags[i].name);
if (cnt > 0)
cpos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
}
}
if (*origpos == '@') {
if (sprintf(cpos++, " ") != 1)
return ERR_UIRC_BUFFER_ERR;
}
*pos = cpos;
return 1;
}
int tok_tags(char* msg, IRC_Message* mesg)
{
if (msg == NULL || mesg == NULL)
return ERR_UIRC_NULL_ARGS;
char *cval, *cpos = msg, *ctag = NULL;
bool clientbound;
const struct tagmapping tags[] = {
{.name = "time", .assg = &mesg->tags.time},
{.name = "account", .assg = &mesg->tags.account},
{.name = "batch", .assg = &mesg->tags.batch},
{.name = "label", .assg = &mesg->tags.label},
{.name = "msgid", .assg = &mesg->tags.msgid},
{.name = "multiline-concat", .assg = &mesg->tags.multiline_concat},
{.name = "typing", .assg = &mesg->tags.typing},
{.name = "react", .assg = &mesg->tags.react},
{.name = "reply", .assg = &mesg->tags.reply}};
if (*cpos == '@')
cpos++;
while ((ctag = strtok_r(NULL, "; ", &cpos)) != NULL) {
clientbound = false;
if (*ctag == '+') {
ctag++;
clientbound = true;
}
if ((cval = strchr(ctag, '=')) != NULL)
*(cval++) = '\0';
for (unsigned int i = 0; i < sizeof(tags) / sizeof(struct tagmapping); i++) {
if (!strcmp(ctag, tags[i].name)) {
if (cval != NULL)
(*tags[i].assg).value = cval;
(*tags[i].assg).clientbound = clientbound;
(*tags[i].assg).present = true;
}
}
}
return 1;
}
int assm_prefix(char** str, IRC_User* usr)
{
if (*str == NULL || usr == NULL)
return ERR_UIRC_NULL_ARGS;
int cnt;
char* pos = *str;
*(pos++) = ':';
if (usr->nick == NULL && usr->user == NULL && usr->host != NULL) {
if ((cnt = sprintf(pos, "%s", usr->host)) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
} else if (usr->nick != NULL) {
if ((cnt = sprintf(pos, "%s", usr->nick)) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
if (usr->user != NULL) {
if ((cnt = sprintf(pos, "!%s", usr->user)) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
}
if (usr->host != NULL) {
if ((cnt = sprintf(pos, "@%s", usr->host)) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
}
} else
return ERR_UIRC_NULL_ARGS;
*(pos++) = ' ';
*str = pos;
return 1;
}
int tok_prefix(char* prefix, IRC_User* user)
{
char* pos = (*prefix == ':') ? prefix + 1 : prefix;
if ((user->host = strchr(pos, '@')) != NULL)
*((char*)user->host++) = '\0';
if ((user->user = strchr(pos, '!')) != NULL) /* RFC2812 says this cannot be here without the host but RFC1459 says it can, we accept both options */
*((char*)user->user++) = '\0';
if (*(user->nick = pos) == '\0') /* NOTE: This may be the server instead of a nick according to RFC2812, it is left to the library user to decide how to interpret this based on the context. */
return ERR_UIRC_INVALID_FORMAT;
return 1;
}