Scan messages for URLs
parent
156282c95d
commit
f502260dd0
1
Makefile
1
Makefile
|
@ -11,6 +11,7 @@ OBJS += edit.o
|
||||||
OBJS += handle.o
|
OBJS += handle.o
|
||||||
OBJS += irc.o
|
OBJS += irc.o
|
||||||
OBJS += ui.o
|
OBJS += ui.o
|
||||||
|
OBJS += url.o
|
||||||
|
|
||||||
dev: tags all
|
dev: tags all
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,15 @@ Close the named, numbered or current window.
|
||||||
Toggle logging in the
|
Toggle logging in the
|
||||||
.Sy <debug>
|
.Sy <debug>
|
||||||
window.
|
window.
|
||||||
|
.It Ic /open Op Ar count
|
||||||
|
Open each of
|
||||||
|
.Ar count
|
||||||
|
most recent URLs.
|
||||||
|
.It Ic /open Ar nick | substring
|
||||||
|
Open the most recent URL from
|
||||||
|
.Ar nick
|
||||||
|
or matching
|
||||||
|
.Ar substring .
|
||||||
.It Ic /window Ar name
|
.It Ic /window Ar name
|
||||||
Switch to window by name.
|
Switch to window by name.
|
||||||
.It Ic /window Ar num , Ic / Ns Ar num
|
.It Ic /window Ar num , Ic / Ns Ar num
|
||||||
|
|
4
chat.h
4
chat.h
|
@ -169,6 +169,10 @@ void completeClear(size_t id);
|
||||||
size_t completeID(const char *str);
|
size_t completeID(const char *str);
|
||||||
enum Color completeColor(size_t id, const char *str);
|
enum Color completeColor(size_t id, const char *str);
|
||||||
|
|
||||||
|
void urlScan(size_t id, const char *nick, const char *mesg);
|
||||||
|
void urlOpenCount(size_t id, size_t count);
|
||||||
|
void urlOpenMatch(size_t id, const char *str);
|
||||||
|
|
||||||
FILE *configOpen(const char *path, const char *mode);
|
FILE *configOpen(const char *path, const char *mode);
|
||||||
int getopt_config(
|
int getopt_config(
|
||||||
int argc, char *const *argv,
|
int argc, char *const *argv,
|
||||||
|
|
11
command.c
11
command.c
|
@ -144,6 +144,16 @@ static void commandClose(size_t id, char *params) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void commandOpen(size_t id, char *params) {
|
||||||
|
if (!params) {
|
||||||
|
urlOpenCount(id, 1);
|
||||||
|
} else if (isdigit(params[0])) {
|
||||||
|
urlOpenCount(id, strtoul(params, NULL, 10));
|
||||||
|
} else {
|
||||||
|
urlOpenMatch(id, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const struct Handler {
|
static const struct Handler {
|
||||||
const char *cmd;
|
const char *cmd;
|
||||||
Command *fn;
|
Command *fn;
|
||||||
|
@ -155,6 +165,7 @@ static const struct Handler {
|
||||||
{ "/names", commandNames },
|
{ "/names", commandNames },
|
||||||
{ "/nick", commandNick },
|
{ "/nick", commandNick },
|
||||||
{ "/notice", commandNotice },
|
{ "/notice", commandNotice },
|
||||||
|
{ "/open", commandOpen },
|
||||||
{ "/part", commandPart },
|
{ "/part", commandPart },
|
||||||
{ "/query", commandQuery },
|
{ "/query", commandQuery },
|
||||||
{ "/quit", commandQuit },
|
{ "/quit", commandQuit },
|
||||||
|
|
15
handle.c
15
handle.c
|
@ -193,6 +193,7 @@ static void handleReplyISupport(struct Message *msg) {
|
||||||
static void handleReplyMOTD(struct Message *msg) {
|
static void handleReplyMOTD(struct Message *msg) {
|
||||||
require(msg, false, 2);
|
require(msg, false, 2);
|
||||||
char *line = msg->params[1];
|
char *line = msg->params[1];
|
||||||
|
urlScan(Network, msg->nick, line);
|
||||||
if (!strncmp(line, "- ", 2)) {
|
if (!strncmp(line, "- ", 2)) {
|
||||||
uiFormat(Network, Cold, tagTime(msg), "\3%d-\3\t%s", Gray, &line[2]);
|
uiFormat(Network, Cold, tagTime(msg), "\3%d-\3\t%s", Gray, &line[2]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,6 +228,7 @@ static void handlePart(struct Message *msg) {
|
||||||
completeClear(id);
|
completeClear(id);
|
||||||
}
|
}
|
||||||
completeRemove(id, msg->nick);
|
completeRemove(id, msg->nick);
|
||||||
|
urlScan(id, msg->nick, msg->params[1]);
|
||||||
uiFormat(
|
uiFormat(
|
||||||
id, Cold, tagTime(msg),
|
id, Cold, tagTime(msg),
|
||||||
"\3%02d%s\3\tleaves \3%02d%s\3%s%s",
|
"\3%02d%s\3\tleaves \3%02d%s\3%s%s",
|
||||||
|
@ -241,6 +243,7 @@ static void handleKick(struct Message *msg) {
|
||||||
size_t id = idFor(msg->params[0]);
|
size_t id = idFor(msg->params[0]);
|
||||||
bool kicked = self.nick && !strcmp(msg->params[1], self.nick);
|
bool kicked = self.nick && !strcmp(msg->params[1], self.nick);
|
||||||
completeTouch(id, msg->nick, hash(msg->user));
|
completeTouch(id, msg->nick, hash(msg->user));
|
||||||
|
urlScan(id, msg->nick, msg->params[2]);
|
||||||
uiFormat(
|
uiFormat(
|
||||||
id, (kicked ? Hot : Cold), tagTime(msg),
|
id, (kicked ? Hot : Cold), tagTime(msg),
|
||||||
"%s\3%02d%s\17\tkicks \3%02d%s\3 out of \3%02d%s\3%s%s",
|
"%s\3%02d%s\17\tkicks \3%02d%s\3 out of \3%02d%s\3%s%s",
|
||||||
|
@ -275,6 +278,7 @@ static void handleQuit(struct Message *msg) {
|
||||||
require(msg, true, 0);
|
require(msg, true, 0);
|
||||||
size_t id;
|
size_t id;
|
||||||
while (None != (id = completeID(msg->nick))) {
|
while (None != (id = completeID(msg->nick))) {
|
||||||
|
urlScan(id, msg->nick, msg->params[0]);
|
||||||
uiFormat(
|
uiFormat(
|
||||||
id, Cold, tagTime(msg),
|
id, Cold, tagTime(msg),
|
||||||
"\3%02d%s\3\tleaves%s%s",
|
"\3%02d%s\3\tleaves%s%s",
|
||||||
|
@ -333,8 +337,10 @@ static void handleReplyTopic(struct Message *msg) {
|
||||||
require(msg, false, 3);
|
require(msg, false, 3);
|
||||||
if (!replies.topic) return;
|
if (!replies.topic) return;
|
||||||
replies.topic--;
|
replies.topic--;
|
||||||
|
size_t id = idFor(msg->params[1]);
|
||||||
|
urlScan(id, NULL, msg->params[2]);
|
||||||
uiFormat(
|
uiFormat(
|
||||||
idFor(msg->params[1]), Cold, tagTime(msg),
|
id, Cold, tagTime(msg),
|
||||||
"The sign in \3%02d%s\3 reads: %s",
|
"The sign in \3%02d%s\3 reads: %s",
|
||||||
hash(msg->params[1]), msg->params[1], msg->params[2]
|
hash(msg->params[1]), msg->params[1], msg->params[2]
|
||||||
);
|
);
|
||||||
|
@ -342,16 +348,18 @@ static void handleReplyTopic(struct Message *msg) {
|
||||||
|
|
||||||
static void handleTopic(struct Message *msg) {
|
static void handleTopic(struct Message *msg) {
|
||||||
require(msg, true, 2);
|
require(msg, true, 2);
|
||||||
|
size_t id = idFor(msg->params[0]);
|
||||||
if (msg->params[1][0]) {
|
if (msg->params[1][0]) {
|
||||||
|
urlScan(id, msg->nick, msg->params[1]);
|
||||||
uiFormat(
|
uiFormat(
|
||||||
idFor(msg->params[0]), Warm, tagTime(msg),
|
id, Warm, tagTime(msg),
|
||||||
"\3%02d%s\3\tplaces a new sign in \3%02d%s\3: %s",
|
"\3%02d%s\3\tplaces a new sign in \3%02d%s\3: %s",
|
||||||
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0],
|
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0],
|
||||||
msg->params[1]
|
msg->params[1]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uiFormat(
|
uiFormat(
|
||||||
idFor(msg->params[0]), Warm, tagTime(msg),
|
id, Warm, tagTime(msg),
|
||||||
"\3%02d%s\3\tremoves the sign in \3%02d%s\3",
|
"\3%02d%s\3\tremoves the sign in \3%02d%s\3",
|
||||||
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0]
|
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0]
|
||||||
);
|
);
|
||||||
|
@ -400,6 +408,7 @@ static void handlePrivmsg(struct Message *msg) {
|
||||||
bool action = isAction(msg);
|
bool action = isAction(msg);
|
||||||
bool mention = !mine && isMention(msg);
|
bool mention = !mine && isMention(msg);
|
||||||
if (!notice && !mine) completeTouch(id, msg->nick, hash(msg->user));
|
if (!notice && !mine) completeTouch(id, msg->nick, hash(msg->user));
|
||||||
|
urlScan(id, msg->nick, msg->params[1]);
|
||||||
if (notice) {
|
if (notice) {
|
||||||
uiFormat(
|
uiFormat(
|
||||||
id, Warm, tagTime(msg),
|
id, Warm, tagTime(msg),
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/* Copyright (C) 2020 C. McEnroe <june@causal.agency>
|
||||||
|
*
|
||||||
|
* This program 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
|
||||||
|
#include "chat.h"
|
||||||
|
|
||||||
|
static const char *Pattern = {
|
||||||
|
"("
|
||||||
|
"cvs|"
|
||||||
|
"ftp|"
|
||||||
|
"git|"
|
||||||
|
"gopher|"
|
||||||
|
"http|"
|
||||||
|
"https|"
|
||||||
|
"irc|"
|
||||||
|
"ircs|"
|
||||||
|
"magnet|"
|
||||||
|
"sftp|"
|
||||||
|
"ssh|"
|
||||||
|
"svn|"
|
||||||
|
"telnet|"
|
||||||
|
"vnc"
|
||||||
|
")"
|
||||||
|
":[^[:space:]>\"]+"
|
||||||
|
};
|
||||||
|
static regex_t Regex;
|
||||||
|
|
||||||
|
static void compile(void) {
|
||||||
|
static bool compiled;
|
||||||
|
if (compiled) return;
|
||||||
|
compiled = true;
|
||||||
|
int error = regcomp(&Regex, Pattern, REG_EXTENDED);
|
||||||
|
if (!error) return;
|
||||||
|
char buf[256];
|
||||||
|
regerror(error, &Regex, buf, sizeof(buf));
|
||||||
|
errx(EX_SOFTWARE, "regcomp: %s: %s", buf, Pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { Cap = 32 };
|
||||||
|
static struct {
|
||||||
|
size_t ids[Cap];
|
||||||
|
char *nicks[Cap];
|
||||||
|
char *urls[Cap];
|
||||||
|
size_t len;
|
||||||
|
} ring;
|
||||||
|
|
||||||
|
static void push(size_t id, const char *nick, const char *url, size_t len) {
|
||||||
|
size_t i = ring.len++ % Cap;
|
||||||
|
free(ring.nicks[i]);
|
||||||
|
free(ring.urls[i]);
|
||||||
|
ring.ids[i] = id;
|
||||||
|
ring.nicks[i] = NULL;
|
||||||
|
if (nick) {
|
||||||
|
ring.nicks[i] = strdup(nick);
|
||||||
|
if (!ring.nicks[i]) err(EX_OSERR, "strdup");
|
||||||
|
}
|
||||||
|
ring.urls[i] = strndup(url, len);
|
||||||
|
if (!ring.urls[i]) err(EX_OSERR, "strndup");
|
||||||
|
}
|
||||||
|
|
||||||
|
void urlScan(size_t id, const char *nick, const char *mesg) {
|
||||||
|
if (!mesg) return;
|
||||||
|
compile();
|
||||||
|
regmatch_t match = {0};
|
||||||
|
for (const char *ptr = mesg; *ptr; ptr += match.rm_eo) {
|
||||||
|
if (regexec(&Regex, ptr, 1, &match, 0)) break;
|
||||||
|
push(id, nick, &ptr[match.rm_so], match.rm_eo - match.rm_so);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void urlOpenCount(size_t id, size_t count) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void urlOpenMatch(size_t id, const char *str) {
|
||||||
|
// TODO
|
||||||
|
}
|
Loading…
Reference in New Issue