/* * 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 . */ #include "tokenizers.h" signed int Tok_mesg(char* str, IRC_Message* out) { if (str == NULL || out == NULL) return ERR_UIRC_NULL_ARGS; int ret; char *progr = str, *command; #ifdef UIRC_IRCV3 if (*progr == '@') { char* tags; if ((tags = strtok_mr(&progr, " ")) != NULL) { if ((ret = Tok_tags(tags, &out->tags)) < 0) return ret; } else return ERR_UIRC_INVALID_FORMAT; } skip_spaces(&progr); #endif if (*progr == ':') { char* prefix; if ((prefix = strtok_mr(&progr, " ")) != NULL) { if ((ret = Tok_user(prefix, &out->name, false)) < 0) return ret; } else return ERR_UIRC_INVALID_FORMAT; } skip_spaces(&progr); if ((command = strtok_mr(&progr, " ")) != NULL) { signed short temp; if (isalpha(*command)) { if ((temp = Ircmd_stoi(command)) < UIRC_FCMD || temp > UIRC_LCMD) return ERR_UIRC_UNKNOWN_TOKEN; out->cmd = (IRC_Command)temp; } else { out->cmd = (IRC_Command)atoi(command); } } else return ERR_UIRC_INVALID_FORMAT; skip_spaces(&progr); short i; for (i = 0; i < 15 && *progr;) { if (i == 14 || *progr == ':') { out->args[i++] = (*progr == ':') ? progr + 1 : progr; out->trailing = true; break; } else { if ((out->args[i++] = strtok_mr(&progr, " ")) == NULL) return ERR_UIRC_INVALID_FORMAT; skip_spaces(&progr); } } out->args[i] = NULL; return 1; } #ifdef UIRC_IRCV3 signed int Tok_tags(char* str, IRC_Tags* out) { if (str == NULL || out == NULL) return ERR_UIRC_NULL_ARGS; char *cval, *cpos = str, *ctag = NULL; bool clientbound; const struct tagmapping tagmps[] = { {.name = "time", .assg = &out->time}, {.name = "account", .assg = &out->account}, {.name = "batch", .assg = &out->batch}, {.name = "label", .assg = &out->label}, {.name = "msgid", .assg = &out->msgid}, {.name = "multiline-concat", .assg = &out->multiline_concat}, {.name = "typing", .assg = &out->typing}, {.name = "react", .assg = &out->react}, {.name = "reply", .assg = &out->reply}}; if (*cpos == '@') cpos++; while ((ctag = strtok_mr(&cpos, "; ")) != NULL) { clientbound = false; if (*ctag == '+') { ctag++; clientbound = true; } if ((cval = strchr(ctag, '=')) != NULL) *(cval++) = '\0'; for (unsigned int i = 0; i < sizeof(tagmps) / sizeof(struct tagmapping); i++) { if (!strcmp(ctag, tagmps[i].name)) { /* If the tag is present we point it to the value if given, or to the delimiter ('\0') * This is done for a few reasons. First, we have a non-null address so we show that * the tag is present and second, we have no value that way */ (*tagmps[i].assg).value = (cval != NULL) ? cval : ctag + strlen(ctag); (*tagmps[i].assg).clientbound = clientbound; } } } return 1; } #endif signed int Tok_user(char* str, IRC_User* out, bool useorig) { char* pos = (*str == ':') ? str + 1 : str; if ((out->host = strchr(pos, '@')) != NULL) *(out->host++) = '\0'; if (useorig && (out->orig = strchr(pos, '%')) != NULL) *(out->orig++) = '\0'; if ((out->user = strchr(pos, '!')) != NULL) /* RFC2812 says this cannot be here without the host but RFC1459 says it can, we accept both options */ *((char*)out->user++) = '\0'; if (!*(out->nick = pos)) return ERR_UIRC_INVALID_FORMAT; /* NOTE: De-facto standard below * This assumes that every prefix without a '@host' and '!user' is itself a host prefix only * It might be a actual nickname, but this is the most common around every network * It is left to the user to decide and handle the result accordingly */ if (out->host == NULL && out->user == NULL) { out->host = out->nick; out->nick = NULL; } return 1; }