Split files better

This commit is contained in:
Alex 2020-07-22 18:57:59 +02:00
parent 5dde15f3b6
commit edd9790cdf
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
18 changed files with 750 additions and 448 deletions

View File

@ -18,10 +18,15 @@ if ( BUILD_TESTS )
enable_testing()
add_subdirectory(tests)
endif()
set(build_FILES src/uirc.c)
set(build_FILES
src/assemblers.c
src/misc.c
src/tokenizers.c
src/validators.c
)
if ( USE_HELPERS )
message("Helper functions are going to be built.")
set(build_FILES src/uirc.c src/helpers.c)
set(build_FILES ${build_FILES} src/helpers.c)
endif()
add_library(uirc SHARED ${build_FILES})
set_property(TARGET uirc PROPERTY C_STANDARD 99)

47
include/functions.h Normal file
View File

@ -0,0 +1,47 @@
/*
* 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 "types.h"
#include <stdbool.h>
#include <sys/types.h>
#ifndef _UIRC_INCLUDED_FUNCT
#define _UIRC_INCLUDED_FUNCT
/* Tokenizers: They take a string in and point their struct element pointers at tokens and end tokens with '\0' */
extern signed int Tok_mesg(char* str, IRC_Message* out);
extern signed int Tok_user(char* str, IRC_User* out, bool useorig);
#ifdef UIRC_IRCV3
extern signed int Tok_tags(char* str, IRC_Tags* out);
#endif
/* Assemblers: They return the amount of bytes written and write directly at buf */
extern signed int Assm_mesg(char* buf, IRC_Message* in, size_t len);
extern signed int Assm_user(char* buf, IRC_User* in, size_t len, bool useorig);
#ifdef UIRC_IRCV3
extern signed int Assm_tags(char* buf, IRC_Tags* in, size_t len);
#endif
/* Validators: They check that the parsed message is valid and follows the standard */
extern signed int Val_mesg(IRC_Message* mesg);
extern signed int Val_channame(char* chan);
extern signed int Val_type_nocrlf(char* str);
extern signed int Val_type_nospcl(char* str);
extern signed int Val_type_noblcm(char* str);
/* Converters: They convert from one format to another */
extern signed int Ircmd_stoi(char* str);
#endif

View File

@ -15,11 +15,12 @@
* You should have received a copy of the GNU General Public License
* along with uIRC. If not, see <https://www.gnu.org/licenses/>.
*/
#include "structs.h"
#include <stdbool.h>
#include "types.h"
#ifdef UIRC_HELPERS
#ifndef _UIRC_HELPERS_INCLUDED
#define _UIRC_HELPERS_INCLUDED
#ifndef _UIRC_INCLUDED_HELPERS
#define _UIRC_INCLUDED_HELPERS
extern IRC_Message* Assm_cmd_NICK(char* nick);
extern IRC_Message* Assm_cmd_USER(char* user, char* realname, int modes);
extern IRC_Message* Assm_cmd_PASS(char* password);

View File

@ -15,8 +15,8 @@
* You should have received a copy of the GNU General Public License
* along with uIRC. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _UIRC_ENUMS_INCLUDED
#define _UIRC_ENUMS_INCLUDED
#ifndef _UIRC_INCLUDED_ENUMS
#define _UIRC_INCLUDED_ENUMS
#define ERR_UIRC_GENERIC -1
#define ERR_UIRC_NULL_ARGS -2
@ -29,9 +29,11 @@
#define MBMASK_WALLOPS 2 /* 010 */
#define MBMASK_INVIS 4 /* 100 */
extern const char* const IRC_Cmds[];
#define UIRC_FCMD ADMIN
enum commands {
ADMIN,
ADMIN = 10,
AWAY,
CONNECT,
DIE,
@ -94,7 +96,9 @@ enum commands {
WARN, /* https://ircv3.net/specs/extensions/standard-replies */
WEBIRC, /* https://ircv3.net/specs/extensions/webirc */
#define UIRC_LCMD WEBIRC
#else
#define UIRC_LCMD WHOWAS
#endif
};

View File

@ -16,16 +16,17 @@
* along with uIRC. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#ifndef _UIRC_STRUCTS_INCLUDED
#define _UIRC_STRUCTS_INCLUDED
#ifndef _UIRC_INCLUDED_TYPES
#define _UIRC_INCLUDED_TYPES
#ifdef UIRC_IRCV3
typedef struct uirc_tag {
typedef struct {
char* value; /* if present, it isn't NULL and if it has no value, it is "" (aka '\0') */
bool clientbound;
} IRC_Tag;
#endif
typedef struct name {
typedef struct {
char* nick;
char* user;
char* host;
@ -35,8 +36,9 @@ typedef struct name {
/* This is how a full user source would look like
* NOTE: 'real (Real name)' may only be used in special contexts other than communication.
* nick!user%orig@host */
#ifdef UIRC_IRCV3
typedef struct uirc_tags {
typedef struct {
/* See https://ircv3.net/registry#tags for more information */
IRC_Tag account;
IRC_Tag batch;
@ -49,7 +51,8 @@ typedef struct uirc_tags {
IRC_Tag reply;
} IRC_Tags;
#endif
typedef struct uirc_message {
typedef struct {
#ifdef UIRC_IRCV3
IRC_Tags tags;
#endif

View File

@ -15,35 +15,13 @@
* You should have received a copy of the GNU General Public License
* along with uIRC. If not, see <https://www.gnu.org/licenses/>.
*/
#include "helpers.h"
#include "types.h"
#include "mappings.h"
#include "structs.h"
#include <stdbool.h>
#include <sys/types.h>
#include "functions.h"
#ifndef UIRC_INCLUDED
#define UIRC_INCLUDED
extern const char* const uirc_ircmd[];
/* Tokenizers: They take a string in and point their struct element pointers at tokens and end tokens with '\0' */
extern signed int Tok_mesg(char* str, IRC_Message* out);
extern signed int Tok_user(char* str, IRC_User* out, bool useorig);
#ifdef UIRC_IRCV3
extern signed int Tok_tags(char* str, IRC_Tags* out);
#endif
/* Assemblers: They return the amount of bytes written and write directly at buf */
extern signed int Assm_mesg(char* buf, IRC_Message* in, size_t len);
extern signed int Assm_user(char* buf, IRC_User* in, size_t len, bool useorig);
#ifdef UIRC_IRCV3
extern signed int Assm_tags(char* buf, IRC_Tags* in, size_t len);
#endif
/* Validators: They check that the parsed message is valid and follows the standard */
extern signed int Val_mesg(IRC_Message* mesg);
extern signed int Val_channame(char* chan);
extern signed int Val_type_nocrlf(char* str);
extern signed int Val_type_nospcl(char* str);
extern signed int Val_type_noblcm(char* str);
extern signed int Ircmd_stoi(char* str);
#define UIRC_VERSION 0.6
#endif

139
src/assemblers.c Normal file
View File

@ -0,0 +1,139 @@
/*
* 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 "assemblers.h"
signed int Assm_mesg(char* buf, IRC_Message* in, size_t len)
{
if (buf == NULL || in == NULL)
return ERR_UIRC_BUFFER_ERR;
char* pos = buf;
unsigned int cnt;
signed int ret;
#ifdef UIRC_IRCV3
if ((ret = Assm_tags(pos, &in->tags, len - (pos - buf))) >= 0)
pos += ret;
else
return ret;
#endif
if (in->name.nick != NULL || in->name.host != NULL) {
if (!safe_charcpy(&pos, ':', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if ((ret = Assm_user(pos, &in->name, len - (pos - buf), false)) <= 0)
return ret;
else
pos += ret;
if (!safe_charcpy(&pos, ' ', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if ((IRC_Cmds[in->cmd] != NULL) && len - (pos - buf) > (cnt = strlen(IRC_Cmds[in->cmd])) + 1 && strcpy(pos, IRC_Cmds[in->cmd]) != NULL)
pos += cnt;
else
return ERR_UIRC_GENERIC;
for (unsigned int i = 0; i < 14 && in->args[i] != NULL; i++) {
if (len - (pos - buf) > strlen(in->args[i]) + 2 && (cnt = snprintf(pos, len - (pos - buf), " %s", in->args[i])) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
}
if (in->trailing != NULL) {
if (len - (pos - buf) > strlen(in->trailing) + 3 && (cnt = snprintf(pos, len - (pos - buf), " :%s", in->trailing)) > 0)
pos += cnt;
else
return ERR_UIRC_GENERIC;
}
if (!safe_strcpy(&pos, "\r\n", len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
return pos - buf;
}
#ifdef UIRC_IRCV3
signed int Assm_tags(char* buf, IRC_Tags* in, size_t len)
{
if (buf == NULL || in == NULL)
return ERR_UIRC_NULL_ARGS;
char* pos = buf;
struct tagmapping tagmps[] = {
{.name = "time", .assg = &in->time},
{.name = "account", .assg = &in->account},
{.name = "batch", .assg = &in->batch},
{.name = "label", .assg = &in->label},
{.name = "msgid", .assg = &in->msgid},
{.name = "multiline-concat", .assg = &in->multiline_concat},
{.name = "typing", .assg = &in->typing},
{.name = "react", .assg = &in->react},
{.name = "reply", .assg = &in->reply}};
for (unsigned int i = 0; i < sizeof(tagmps) / sizeof(struct tagmapping); i++) {
if ((*tagmps[i].assg).value != NULL) {
if (pos == buf) {
if (!safe_charcpy(&pos, '@', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
} else {
if (!safe_charcpy(&pos, ';', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if ((*tagmps[i].assg).clientbound) {
if (!safe_charcpy(&pos, '+', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if (!safe_strcpy(&pos, tagmps[i].name, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (*(*tagmps[i].assg).value != '\0') {
if (!safe_charcpy(&pos, '=', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, (*tagmps[i].assg).value, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
}
}
return pos - buf;
}
#endif
signed int Assm_user(char* buf, IRC_User* in, size_t len, bool useorig)
{
if (buf == NULL || in == NULL)
return ERR_UIRC_NULL_ARGS;
char* pos = buf;
if (in->nick == NULL && in->host != NULL) {
if (!safe_strcpy(&pos, in->host, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
} else if (in->nick != NULL) {
if (!safe_strcpy(&pos, in->nick, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (in->user != NULL) {
if (!safe_charcpy(&pos, '!', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, in->user, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if (useorig && in->orig != NULL) {
if (!safe_charcpy(&pos, '%', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, in->orig, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if (in->host != NULL) {
if (!safe_charcpy(&pos, '@', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, in->host, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
} else
return ERR_UIRC_NULL_ARGS;
return pos - buf;
}

33
src/assemblers.h Normal file
View File

@ -0,0 +1,33 @@
/*
* 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 "../include/types.h"
#include "../include/mappings.h"
#include "misc.h"
#include <stdio.h>
#include <string.h>
#ifndef _UIRC_INCLUDED_ASSM
#define _UIRC_INCLUDED_ASSM
signed int Assm_mesg(char* buf, IRC_Message* in, size_t len);
#ifdef UIRC_IRCV3
signed int Assm_tags(char* buf, IRC_Tags* in, size_t len);
#endif
signed int Assm_user(char* buf, IRC_User* in, size_t len, bool useorig);
#endif

View File

@ -15,11 +15,9 @@
* You should have received a copy of the GNU General Public License
* along with uIRC. If not, see <https://www.gnu.org/licenses/>.
*/
#include "../include/uirc.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "helpers.h"
static IRC_Message imassm_mesg;
char* RESERVED = "*";
void clear_assm(void)

77
src/helpers.h Normal file
View File

@ -0,0 +1,77 @@
/*
* 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 "../include/types.h"
#include "../include/mappings.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _UIRC_INCLUDED_ASSM
#define _UIRC_INCLUDED_ASSM
void clear_assm(void);
IRC_Message* Assm_cmd_NICK(char* nick);
IRC_Message* Assm_cmd_USER(char* user, char* realname, int modes);
IRC_Message* Assm_cmd_PASS(char* password);
IRC_Message* Assm_cmd_OPER(char* name, char* password);
IRC_Message* Assm_cmd_MODE(char* nick, char* modes, char* modeparams);
IRC_Message* Assm_cmd_SERVICE(char* nickname, char* distribution, char* type, char* info);
IRC_Message* Assm_cmd_QUIT(char* mesg);
IRC_Message* Assm_cmd_SQUIT(char* server, char* comment);
IRC_Message* Assm_cmd_JOIN(char* channels, char* keys);
IRC_Message* Assm_cmd_PART(char* channel, char* message);
IRC_Message* Assm_cmd_TOPIC(char* channel, char* topic);
IRC_Message* Assm_cmd_NAMES(char* channels, char* target);
IRC_Message* Assm_cmd_LIST(char* channels, char* target);
IRC_Message* Assm_cmd_INVITE(char* nick, char* channel);
IRC_Message* Assm_cmd_KICK(char* channels, char* users, char* comment);
IRC_Message* Assm_cmd_PRIVMSG(char* target, char* message);
IRC_Message* Assm_cmd_NOTICE(char* target, char* text);
IRC_Message* Assm_cmd_MOTD(char* target);
IRC_Message* Assm_cmd_LUSERS(char* mask, char* target);
IRC_Message* Assm_cmd_VERSION(char* target);
IRC_Message* Assm_cmd_STATS(char* query, char* target);
IRC_Message* Assm_cmd_LINKS(char* remoteserv, char* servmask);
IRC_Message* Assm_cmd_TIME(char* target);
IRC_Message* Assm_cmd_CONNECT(char* target, char* port, char* remote);
IRC_Message* Assm_cmd_TRACE(char* target);
IRC_Message* Assm_cmd_ADMIN(char* target);
IRC_Message* Assm_cmd_INFO(char* target);
IRC_Message* Assm_cmd_SERVLIST(char* mask, char* type);
IRC_Message* Assm_cmd_SQUERY(char* servicename, char* text);
IRC_Message* Assm_cmd_WHO(char* mask, bool oper);
IRC_Message* Assm_cmd_WHOIS(char* target, char* mask);
IRC_Message* Assm_cmd_WHOWAS(char* nick, char* count, char* target);
IRC_Message* Assm_cmd_KILL(char* nick, char* comment);
IRC_Message* Assm_cmd_PING(char* source, char* target);
IRC_Message* Assm_cmd_PONG(char* source, char* target);
IRC_Message* Assm_cmd_ERROR(char* message);
IRC_Message* Assm_cmd_AWAY(char* mesg);
IRC_Message* Assm_cmd_REHASH(void);
IRC_Message* Assm_cmd_DIE(void);
IRC_Message* Assm_cmd_RESTART(void);
IRC_Message* Assm_cmd_SUMMON(char* user, char* target, char* channel);
IRC_Message* Assm_cmd_USERS(char* target);
IRC_Message* Assm_cmd_WALLOPS(char* text);
IRC_Message* Assm_cmd_USERHOST(char* users[]);
IRC_Message* Assm_cmd_ISON(char* users[]);
void Tok_cmd_PING(IRC_Message* mesg, char** source, char** target);
void Tok_FArgOpt(IRC_Message* mesg, char** optarg, char** reqarg);
#endif

116
src/misc.c Normal file
View File

@ -0,0 +1,116 @@
/*
* 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 "misc.h"
const char* const IRC_Cmds[] = {
[ADMIN] = "ADMIN",
[AWAY] = "AWAY",
[CONNECT] = "CONNECT",
[DIE] = "DIE",
[ERROR] = "ERROR",
[INFO] = "INFO",
[INVITE] = "INVITE",
[ISON] = "ISON",
[JOIN] = "JOIN",
[KICK] = "KICK",
[KILL] = "KILL",
[LINKS] = "LINKS",
[LIST] = "LIST",
[LUSERS] = "LUSERS",
[MODE] = "MODE",
[MOTD] = "MOTD",
[NAMES] = "NAMES",
[NICK] = "NICK",
[NOTICE] = "NOTICE",
[OPER] = "OPER",
[PART] = "PART",
[PASS] = "PASS",
[PING] = "PING",
[PONG] = "PONG",
[PRIVMSG] = "PRIVMSG",
[QUIT] = "QUIT",
[REHASH] = "REHASH",
[RESTART] = "RESTART",
[SERVER] = "SERVER",
[SERVICE] = "SERVICE",
[SERVLIST] = "SERVLIST",
[SQUERY] = "SQUERY",
[SQUIT] = "SQUIT",
[STATS] = "STATS",
[SUMMON] = "SUMMON",
[TIME] = "TIME",
[TOPIC] = "TOPIC",
[TRACE] = "TRACE",
[USERHOST] = "USERHOST",
[USERS] = "USERS",
[USER] = "USER",
[VERSION] = "VERSION",
[WALLOPS] = "WALLOPS",
[WHOIS] = "WHOIS",
[WHOWAS] = "WHOWAS",
[WHO] = "WHO",
#ifdef UIRC_IRCV3
[ACCOUNT] = "ACCOUNT",
[ACC] = "ACC",
[ACK] = "ACK",
[AUTHENTICATE] = "AUTHENTICATE",
[BATCH] = "BATCH",
[CAP] = "CAP",
[CHGHOST] = "CHGHOST",
[FAIL] = "FAIL",
[MONITOR] = "MONITOR",
[NOTE] = "NOTE",
[RENAME] = "RENAME",
[RESUME] = "RESUME",
[SETNAME] = "SETNAME",
[WARN] = "WARN",
[WEBIRC] = "WEBIRC"
#endif
};
signed int Ircmd_stoi(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (unsigned short i = UIRC_FCMD; i <= UIRC_LCMD; i++) {
if (IRC_Cmds[i] != NULL && strcmp(IRC_Cmds[i], str) == 0)
return i;
}
return ERR_UIRC_UNKNOWN_TOKEN;
}
int safe_strcpy(char** dest, const char* src, size_t lef)
{
size_t cnt;
if (lef > (cnt = strlen(src)) + 1) {
strcpy(*dest, src);
*dest += cnt;
return cnt;
} else
return 0;
}
int safe_charcpy(char** dest, const char c, size_t lef)
{
if (lef > 1) {
*(*dest)++ = c;
**dest = '\0';
return 1;
} else
return 0;
}

36
src/misc.h Normal file
View File

@ -0,0 +1,36 @@
/*
* 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 "../include/mappings.h"
#include "../include/types.h"
#include <stdio.h>
#include <string.h>
#ifndef _UIRC_INCLUDED_MISC
#define _UIRC_INCLUDED_MISC
#ifdef UIRC_IRCV3
struct tagmapping {
const char* const name;
IRC_Tag* assg;
};
#endif
signed int Ircmd_stoi(char* str);
int safe_strcpy(char** dest, const char* src, size_t lef);
int safe_charcpy(char** dest, const char c, size_t lef);
#endif

117
src/tokenizers.c Normal file
View File

@ -0,0 +1,117 @@
/*
* 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 "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_r(progr, " ", &progr)) != NULL) {
if ((ret = Tok_tags(tags, &out->tags)) < 0)
return ret;
} else
return ERR_UIRC_INVALID_FORMAT;
}
#endif
if (*progr == ':') {
char* prefix;
if ((prefix = strtok_r(progr, " ", &progr)) != NULL) {
if ((ret = Tok_user(prefix, &out->name, false)) < 0)
return ret;
} else
return ERR_UIRC_INVALID_FORMAT;
}
if ((command = strtok_r(NULL, " ", &progr)) != NULL) {
if (!(out->cmd = (isalpha(*command)) ? Ircmd_stoi(command) : atoi(command)))
return ERR_UIRC_UNKNOWN_TOKEN;
} else {
return ERR_UIRC_INVALID_FORMAT;
}
int i;
for (i = 0; *progr != ':' && i < 14 && *progr; i++) {
if ((out->args[i] = strtok_r(NULL, " ", &progr)) == NULL)
return ERR_UIRC_INVALID_FORMAT;
}
out->args[i] = NULL;
if (*progr)
out->trailing = (*progr == ':') ? progr + 1 : progr;
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_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(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)) /* NOTE: This may be the server instead of a nick according to RFC2812, it is left to the library out to decide how to interpret this based on the context. */
return ERR_UIRC_INVALID_FORMAT;
return 1;
}

35
src/tokenizers.h Normal file
View File

@ -0,0 +1,35 @@
/*
* 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 "../include/types.h"
#include "../include/mappings.h"
#include "misc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef _UIRC_INCLUDED_TKNIZ
#define _UIRC_INCLUDED_TKNIZ
signed int Tok_mesg(char* str, IRC_Message* out);
#ifdef UIRC_IRCV3
signed int Tok_tags(char* str, IRC_Tags* out);
#endif
signed int Tok_user(char* str, IRC_User* out, bool useorig);
#endif

View File

@ -1,401 +0,0 @@
/*
* 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[] = {
[ADMIN] = "ADMIN",
[AWAY] = "AWAY",
[CONNECT] = "CONNECT",
[DIE] = "DIE",
[ERROR] = "ERROR",
[INFO] = "INFO",
[INVITE] = "INVITE",
[ISON] = "ISON",
[JOIN] = "JOIN",
[KICK] = "KICK",
[KILL] = "KILL",
[LINKS] = "LINKS",
[LIST] = "LIST",
[LUSERS] = "LUSERS",
[MODE] = "MODE",
[MOTD] = "MOTD",
[NAMES] = "NAMES",
[NICK] = "NICK",
[NOTICE] = "NOTICE",
[OPER] = "OPER",
[PART] = "PART",
[PASS] = "PASS",
[PING] = "PING",
[PONG] = "PONG",
[PRIVMSG] = "PRIVMSG",
[QUIT] = "QUIT",
[REHASH] = "REHASH",
[RESTART] = "RESTART",
[SERVER] = "SERVER",
[SERVICE] = "SERVICE",
[SERVLIST] = "SERVLIST",
[SQUERY] = "SQUERY",
[SQUIT] = "SQUIT",
[STATS] = "STATS",
[SUMMON] = "SUMMON",
[TIME] = "TIME",
[TOPIC] = "TOPIC",
[TRACE] = "TRACE",
[USERHOST] = "USERHOST",
[USERS] = "USERS",
[USER] = "USER",
[VERSION] = "VERSION",
[WALLOPS] = "WALLOPS",
[WHOIS] = "WHOIS",
[WHOWAS] = "WHOWAS",
[WHO] = "WHO",
#ifdef UIRC_IRCV3
[ACCOUNT] = "ACCOUNT",
[ACC] = "ACC",
[ACK] = "ACK",
[AUTHENTICATE] = "AUTHENTICATE",
[BATCH] = "BATCH",
[CAP] = "CAP",
[CHGHOST] = "CHGHOST",
[FAIL] = "FAIL",
[MONITOR] = "MONITOR",
[NOTE] = "NOTE",
[RENAME] = "RENAME",
[RESUME] = "RESUME",
[SETNAME] = "SETNAME",
[WARN] = "WARN",
[WEBIRC] = "WEBIRC"
#endif
};
#ifdef UIRC_IRCV3
struct tagmapping {
const char* const name;
IRC_Tag* assg;
};
#endif
signed int Ircmd_stoi(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (unsigned short i = UIRC_FCMD; i <= UIRC_LCMD; i++) {
if (uirc_ircmd[i] != NULL && strcmp(uirc_ircmd[i], str) == 0)
return i;
}
return ERR_UIRC_UNKNOWN_TOKEN;
}
signed int Assm_mesg(char* buf, IRC_Message* in, size_t len)
{
if (buf == NULL || in == NULL)
return ERR_UIRC_BUFFER_ERR;
char* pos = buf;
unsigned int cnt;
signed int ret;
#ifdef UIRC_IRCV3
if ((ret = Assm_tags(pos, &in->tags, len - (pos - buf))) >= 0)
pos += ret;
else
return ret;
#endif
if (in->name.nick != NULL || in->name.host != NULL) {
if (!safe_charcpy(&pos, ':', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if ((ret = Assm_user(pos, &in->name, len - (pos - buf), false)) <= 0)
return ret;
else
pos += ret;
if (!safe_charcpy(&pos, ' ', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if ((uirc_ircmd[in->cmd] != NULL) && len - (pos - buf) > (cnt = strlen(uirc_ircmd[in->cmd])) + 1 && strcpy(pos, uirc_ircmd[in->cmd]) != NULL)
pos += cnt;
else
return ERR_UIRC_GENERIC;
for (unsigned int i = 0; i < 14 && in->args[i] != NULL; i++) {
if (len - (pos - buf) > strlen(in->args[i]) + 2 && (cnt = snprintf(pos, len - (pos - buf), " %s", in->args[i])) > 0)
pos += cnt;
else
return ERR_UIRC_BUFFER_ERR;
}
if (in->trailing != NULL) {
if (len - (pos - buf) > strlen(in->trailing) + 3 && (cnt = snprintf(pos, len - (pos - buf), " :%s", in->trailing)) > 0)
pos += cnt;
else
return ERR_UIRC_GENERIC;
}
if (!safe_strcpy(&pos, "\r\n", len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
return pos - buf;
}
#ifdef UIRC_IRCV3
signed int Assm_tags(char* buf, IRC_Tags* in, size_t len)
{
if (buf == NULL || in == NULL)
return ERR_UIRC_NULL_ARGS;
char* pos = buf;
struct tagmapping tagmps[] = {
{.name = "time", .assg = &in->time},
{.name = "account", .assg = &in->account},
{.name = "batch", .assg = &in->batch},
{.name = "label", .assg = &in->label},
{.name = "msgid", .assg = &in->msgid},
{.name = "multiline-concat", .assg = &in->multiline_concat},
{.name = "typing", .assg = &in->typing},
{.name = "react", .assg = &in->react},
{.name = "reply", .assg = &in->reply}};
for (unsigned int i = 0; i < sizeof(tagmps) / sizeof(struct tagmapping); i++) {
if ((*tagmps[i].assg).value != NULL) {
if (pos == buf) {
if (!safe_charcpy(&pos, '@', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
} else {
if (!safe_charcpy(&pos, ';', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if ((*tagmps[i].assg).clientbound) {
if (!safe_charcpy(&pos, '+', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if (!safe_strcpy(&pos, tagmps[i].name, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (*(*tagmps[i].assg).value != '\0') {
if (!safe_charcpy(&pos, '=', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, (*tagmps[i].assg).value, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
}
}
return pos - buf;
}
#endif
signed int Assm_user(char* buf, IRC_User* in, size_t len, bool useorig)
{
if (buf == NULL || in == NULL)
return ERR_UIRC_NULL_ARGS;
char* pos = buf;
if (in->nick == NULL && in->host != NULL) {
if (!safe_strcpy(&pos, in->host, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
} else if (in->nick != NULL) {
if (!safe_strcpy(&pos, in->nick, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (in->user != NULL) {
if (!safe_charcpy(&pos, '!', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, in->user, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if (useorig && in->orig != NULL) {
if (!safe_charcpy(&pos, '%', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, in->orig, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
if (in->host != NULL) {
if (!safe_charcpy(&pos, '@', len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
if (!safe_strcpy(&pos, in->host, len - (pos - buf)))
return ERR_UIRC_BUFFER_ERR;
}
} else
return ERR_UIRC_NULL_ARGS;
return pos - buf;
}
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_r(progr, " ", &progr)) != NULL) {
if ((ret = Tok_tags(tags, &out->tags)) < 0)
return ret;
} else
return ERR_UIRC_INVALID_FORMAT;
}
#endif
if (*progr == ':') {
char* prefix;
if ((prefix = strtok_r(progr, " ", &progr)) != NULL) {
if ((ret = Tok_user(prefix, &out->name, false)) < 0)
return ret;
} else
return ERR_UIRC_INVALID_FORMAT;
}
if ((command = strtok_r(NULL, " ", &progr)) != NULL) {
if (!(out->cmd = (isalpha(*command)) ? Ircmd_stoi(command) : atoi(command)))
return ERR_UIRC_UNKNOWN_TOKEN;
} else {
return ERR_UIRC_INVALID_FORMAT;
}
int i;
for (i = 0; *progr != ':' && i < 14 && *progr; i++) {
if ((out->args[i] = strtok_r(NULL, " ", &progr)) == NULL)
return ERR_UIRC_INVALID_FORMAT;
}
out->args[i] = NULL;
if (*progr)
out->trailing = (*progr == ':') ? progr + 1 : progr;
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_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(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)) /* NOTE: This may be the server instead of a nick according to RFC2812, it is left to the library out to decide how to interpret this based on the context. */
return ERR_UIRC_INVALID_FORMAT;
return 1;
}
signed int Val_mesg(IRC_Message* mesg)
{
if (mesg == NULL)
return ERR_UIRC_NULL_ARGS;
for (unsigned int i = 0; i < 14 && mesg->args[i] != NULL; i++) {
if (Val_type_nocrlf(mesg->args[i]) != 1)
return ERR_UIRC_VAL_FAILED;
if (Val_type_nospcl(mesg->args[i]) != 1)
return ERR_UIRC_VAL_FAILED;
}
if (mesg->trailing != NULL) {
if (Val_type_nocrlf(mesg->trailing) != 1)
return ERR_UIRC_VAL_FAILED;
}
return 1;
}
signed int Val_type_nocrlf(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (; *str; str++) {
if (*str == '\r' || *str == '\n')
return 0;
}
return 1;
}
signed int Val_type_nospcl(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (; *str; str++) {
if (*str == ' ' || *str == ':')
return 0;
}
return 1;
}
signed int Val_type_noblcm(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (; *str; str++) {
if (*str == '\a' || *str == ',')
return 0;
}
return 1;
}
signed int Val_channame(char* chan)
{
if (chan == NULL)
return ERR_UIRC_NULL_ARGS;
if (*chan != '#' || *chan != '+' || *chan != '!' || *chan != '&')
return 0;
char* clps = ++chan;
if ((clps = strchr(chan, ':')) != NULL) {
*clps = '\0';
if (Val_type_nospcl(chan) != 1 || Val_type_nocrlf(chan) != 1 || Val_type_noblcm(chan) != 1) {
*clps = ':';
return 0;
}
*clps = ':';
chan = ++clps;
}
if (Val_type_nospcl(chan) != 1 || Val_type_nocrlf(chan) != 1 || Val_type_noblcm(chan) != 1)
return 0;
return 1;
}
int safe_strcpy(char** dest, const char* src, size_t lef)
{
size_t cnt;
if (lef > (cnt = strlen(src)) + 1) {
strcpy(*dest, src);
*dest += cnt;
return cnt;
} else
return 0;
}
int safe_charcpy(char** dest, const char c, size_t lef)
{
if (lef > 1) {
*(*dest)++ = c;
**dest = '\0';
return 1;
} else
return 0;
}

View File

@ -21,5 +21,3 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int safe_strcpy(char** dest, const char* src, size_t lef);
int safe_charcpy(char** dest, const char c, size_t lef);

90
src/validators.c Normal file
View File

@ -0,0 +1,90 @@
/*
* 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 "validators.h"
signed int Val_mesg(IRC_Message* mesg)
{
if (mesg == NULL)
return ERR_UIRC_NULL_ARGS;
for (unsigned int i = 0; i < 14 && mesg->args[i] != NULL; i++) {
if (Val_type_nocrlf(mesg->args[i]) != 1)
return ERR_UIRC_VAL_FAILED;
if (Val_type_nospcl(mesg->args[i]) != 1)
return ERR_UIRC_VAL_FAILED;
}
if (mesg->trailing != NULL) {
if (Val_type_nocrlf(mesg->trailing) != 1)
return ERR_UIRC_VAL_FAILED;
}
return 1;
}
signed int Val_type_nocrlf(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (; *str; str++) {
if (*str == '\r' || *str == '\n')
return 0;
}
return 1;
}
signed int Val_type_nospcl(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (; *str; str++) {
if (*str == ' ' || *str == ':')
return 0;
}
return 1;
}
signed int Val_type_noblcm(char* str)
{
if (str == NULL)
return ERR_UIRC_NULL_ARGS;
for (; *str; str++) {
if (*str == '\a' || *str == ',')
return 0;
}
return 1;
}
signed int Val_channame(char* chan)
{
if (chan == NULL)
return ERR_UIRC_NULL_ARGS;
if (*chan != '#' || *chan != '+' || *chan != '!' || *chan != '&')
return 0;
char* clps = ++chan;
if ((clps = strchr(chan, ':')) != NULL) {
*clps = '\0';
if (Val_type_nospcl(chan) != 1 || Val_type_nocrlf(chan) != 1 || Val_type_noblcm(chan) != 1) {
*clps = ':';
return 0;
}
*clps = ':';
chan = ++clps;
}
if (Val_type_nospcl(chan) != 1 || Val_type_nocrlf(chan) != 1 || Val_type_noblcm(chan) != 1)
return 0;
return 1;
}

26
src/validators.h Normal file
View File

@ -0,0 +1,26 @@
/*
* 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 "../include/types.h"
#include "../include/mappings.h"
#include <stdio.h>
#include <string.h>
signed int Val_mesg(IRC_Message* mesg);
signed int Val_type_nocrlf(char* str);
signed int Val_type_nospcl(char* str);
signed int Val_type_noblcm(char* str);
signed int Val_channame(char* chan);