Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for message-tags #337

Merged
merged 3 commits into from
Nov 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/core/app_irc_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ static void irc_event_privmsg(SircSession *sirc, const char *event,
const char *origin, const char *params[], int count);
static void irc_event_notice(SircSession *sirc, const char *event,
const char *origin, const char *params[], int count);
static void irc_event_tagmsg(SircSession *sirc, const char *event,
SilverRainZ marked this conversation as resolved.
Show resolved Hide resolved
const char *origin, const char *params[], int count);
static void irc_event_channel_notice(SircSession *sirc, const char *event,
const char *origin, const char *params[], int count);
static void irc_event_invite(SircSession *sirc, const char *event,
Expand Down Expand Up @@ -113,6 +115,7 @@ void srn_application_init_irc_event(SrnApplication *app) {
app->irc_events.channel = irc_event_channel;
app->irc_events.privmsg = irc_event_privmsg;
app->irc_events.notice = irc_event_notice;
app->irc_events.tagmsg = irc_event_tagmsg;
app->irc_events.channel_notice = irc_event_channel_notice;
app->irc_events.invite = irc_event_invite;
app->irc_events.ctcp_req = irc_event_ctcp_req;
Expand Down Expand Up @@ -824,6 +827,11 @@ static void irc_event_notice(SircSession *sirc, const char *event,
srn_chat_add_notice_message(chat, chat_user, msg);
}

static void irc_event_tagmsg(SircSession *sirc, const char *event,
const char *origin, const char **params, int count){
/* Not used yet */
}

static void irc_event_channel_notice(SircSession *sirc, const char *event,
const char *origin, const char **params, int count){
const char *chan;
Expand Down
4 changes: 4 additions & 0 deletions src/core/server_cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ static ServerCapSupport supported_caps[] = {
},

// /* IRCv3.2 */
{
.name = "message-tags",
.offset = offsetof(EnabledCap, message_tags),
},
// {
// .name = "server-time",
// .offset = offsetof(EnabledCap, server_time),
Expand Down
1 change: 1 addition & 0 deletions src/inc/core/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ struct _EnabledCap {
bool sasl;

// Version 3.2
bool message_tags;
bool server_time;
bool userhost_in_names;
bool cap_notify;
Expand Down
1 change: 1 addition & 0 deletions src/inc/sirc/sirc_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ typedef struct {
SircEventCallback channel;
SircEventCallback privmsg;
SircEventCallback notice;
SircEventCallback tagmsg;
SircEventCallback channel_notice;
SircEventCallback invite;
SircEventCallback ctcp_req;
Expand Down
4 changes: 4 additions & 0 deletions src/sirc/sirc_event_hdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ void sirc_event_hdr(SircSession *sirc, SircMessage *imsg){
g_return_if_fail(events->error);
events->error(sirc, event, origin, params, imsg->nparam);
}
else if (strcasecmp(event, "TAGMSG") == 0){
g_return_if_fail(events->error);
events->tagmsg(sirc, event, origin, params, imsg->nparam);
}
else {
g_return_if_fail(events->unknown);
events->unknown(sirc, event, origin, params, imsg->nparam);
Expand Down
125 changes: 119 additions & 6 deletions src/sirc/sirc_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include "log.h"
#include "utils.h"

/* https://ircv3.net/specs/extensions/message-tags#size-limit */
#define TAGS_SIZE_LIMIT 8191

SircMessage *sirc_message_new(){
return g_malloc0(sizeof(SircMessage));
}
Expand All @@ -50,6 +53,15 @@ void sirc_message_free(SircMessage *imsg){
str_assign(&imsg->params[i], NULL);
}

for (int i = 0; i < imsg->ntags; i++){
str_assign(&imsg->tags[i].key, NULL);
str_assign(&imsg->tags[i].value, NULL);
}

if (imsg->ntags > 0) {
g_free(imsg->tags);
}

g_free(imsg);
}

Expand All @@ -63,6 +75,9 @@ void sirc_message_transcoding(SircMessage *imsg, const char *from_codeset) {
for (int i = 0; i < imsg->nparam; i++){
str_transcoding(&imsg->params[i], from_codeset);
}

/* No need to transcode tags, they are guaranteed to be UTF-8
* by https://ircv3.net/specs/extensions/message-tags */
}

/**
Expand All @@ -80,20 +95,118 @@ SircMessage* sirc_parse(char *line){
imsg = sirc_message_new();

/* This is a IRC message
* IRS protocol message format?
* See: https://tools.ietf.org/html/rfc1459#section-2.3
* IRC protocol message format?
* See: https://ircv3.net/specs/extensions/message-tags
*/
char *rfc1459_line;
char *tags_ptr;
char *prefix_ptr, *command_ptr;
char *trailing_ptr, *params_ptr;
char *nick_ptr, *user_ptr, *host_ptr;

// <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
if (line[0] == ':'){
prefix_ptr = strtok(line + 1, " "); // Skip ':'
// <message> ::= ['@' <tags> <SPACE> ] [':' <prefix> <SPACE> ] <command> <params> <crlf>
if (line[0] == '@'){
size_t ntags;
tags_ptr = line + 1;
rfc1459_line = strchr(line, ' ') + 1;

/* Count the number of tags to allocate a tag array */
ntags = 1;
for (char *p=tags_ptr; *p != ' '; p++){
if (*p == ';'){
ntags++;
}
}

imsg->ntags = ntags;
imsg->tags = g_malloc0_n(ntags, sizeof(SircMessageTag));
size_t i=0;
char current_tag_key[TAGS_SIZE_LIMIT];
char current_tag_value[TAGS_SIZE_LIMIT];
char *current_tag_key_ptr = current_tag_key;
char *current_tag_value_ptr = current_tag_value;
gboolean in_key = TRUE;
for (char *p=tags_ptr; ; p++){
if (*p == '\0') {
ERR_FR("Unexpected null byte in message tags");
goto bad;
SilverRainZ marked this conversation as resolved.
Show resolved Hide resolved
}
if (p >= (tags_ptr + TAGS_SIZE_LIMIT)) {
ERR_FR("Message tag exceeds maximum size");
goto bad;
SilverRainZ marked this conversation as resolved.
Show resolved Hide resolved
}

if (*p == ';' || *p == ' '){
/* next tag or end of tags*/
in_key = TRUE;
*current_tag_key_ptr = '\0';
imsg->tags[i].key = strdup(current_tag_key);
if (current_tag_value == current_tag_value_ptr){
/* Key is absent or empty */
imsg->tags[i].value = NULL;
}
else {
*current_tag_value_ptr = '\0';
imsg->tags[i].value = strdup(current_tag_value);
}
if (*p == ' '){
/* end of tags */
break;
}
i++;

current_tag_key_ptr = current_tag_key;
current_tag_value_ptr = current_tag_value;
}
else if (*p == '=' && in_key){
/* tag's value */
in_key = FALSE;
}
else if (in_key){
*(current_tag_key_ptr++) = *p;
}
else if (!in_key && *p == '\\'){
/* Possibly an escaped character in the value
* https://ircv3.net/specs/extensions/message-tags#escaping-values
*/
p++;
if (*p == ':')
*(current_tag_value_ptr++) = ';';
else if (*p == 's')
*(current_tag_value_ptr++) = ' ';
else if (*p == '\\')
*(current_tag_value_ptr++) = '\\';
else if (*p == 'r')
*(current_tag_value_ptr++) = '\r';
else if (*p == 'n')
*(current_tag_value_ptr++) = '\n';
else{
/* "If a \ exists with no valid escape character (for example, \b),
* then the invalid backslash SHOULD be dropped.
* For example, \b should unescape to just b."
*/
*(current_tag_value_ptr++) = *p;
}
}
else{
*(current_tag_value_ptr++) = *p;
}
}
}
else{
imsg->tags = NULL;
imsg->ntags = 0;
rfc1459_line = line;
}

/* Now parse like in RFC1459 */

if (rfc1459_line[0] == ':'){
prefix_ptr = strtok(rfc1459_line + 1, " "); // Skip ':'
command_ptr = strtok(NULL, " ");
} else {
prefix_ptr = NULL;
command_ptr = strtok(line, " ");
command_ptr = strtok(rfc1459_line, " ");
}

params_ptr = strtok(NULL, "");
Expand Down
8 changes: 8 additions & 0 deletions src/sirc/sirc_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
#define SIRC_PARAM_COUNT 64 // RFC 2812 limits it to 14

typedef struct {
char *key;
char *value; // possibly NULL
} SircMessageTag;

typedef struct {
size_t ntags;
SircMessageTag *tags;

char *prefix; // servername or nick!user@host
char *nick, *user, *host;

Expand Down