Scan messages for URLs
parent
156282c95d
commit
f502260dd0
1
Makefile
1
Makefile
|
@ -11,6 +11,7 @@ OBJS += edit.o
|
|||
OBJS += handle.o
|
||||
OBJS += irc.o
|
||||
OBJS += ui.o
|
||||
OBJS += url.o
|
||||
|
||||
dev: tags all
|
||||
|
||||
|
|
|
@ -156,6 +156,15 @@ Close the named, numbered or current window.
|
|||
Toggle logging in the
|
||||
.Sy <debug>
|
||||
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
|
||||
Switch to window by name.
|
||||
.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);
|
||||
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);
|
||||
int getopt_config(
|
||||
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 {
|
||||
const char *cmd;
|
||||
Command *fn;
|
||||
|
@ -155,6 +165,7 @@ static const struct Handler {
|
|||
{ "/names", commandNames },
|
||||
{ "/nick", commandNick },
|
||||
{ "/notice", commandNotice },
|
||||
{ "/open", commandOpen },
|
||||
{ "/part", commandPart },
|
||||
{ "/query", commandQuery },
|
||||
{ "/quit", commandQuit },
|
||||
|
|
15
handle.c
15
handle.c
|
@ -193,6 +193,7 @@ static void handleReplyISupport(struct Message *msg) {
|
|||
static void handleReplyMOTD(struct Message *msg) {
|
||||
require(msg, false, 2);
|
||||
char *line = msg->params[1];
|
||||
urlScan(Network, msg->nick, line);
|
||||
if (!strncmp(line, "- ", 2)) {
|
||||
uiFormat(Network, Cold, tagTime(msg), "\3%d-\3\t%s", Gray, &line[2]);
|
||||
} else {
|
||||
|
@ -227,6 +228,7 @@ static void handlePart(struct Message *msg) {
|
|||
completeClear(id);
|
||||
}
|
||||
completeRemove(id, msg->nick);
|
||||
urlScan(id, msg->nick, msg->params[1]);
|
||||
uiFormat(
|
||||
id, Cold, tagTime(msg),
|
||||
"\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]);
|
||||
bool kicked = self.nick && !strcmp(msg->params[1], self.nick);
|
||||
completeTouch(id, msg->nick, hash(msg->user));
|
||||
urlScan(id, msg->nick, msg->params[2]);
|
||||
uiFormat(
|
||||
id, (kicked ? Hot : Cold), tagTime(msg),
|
||||
"%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);
|
||||
size_t id;
|
||||
while (None != (id = completeID(msg->nick))) {
|
||||
urlScan(id, msg->nick, msg->params[0]);
|
||||
uiFormat(
|
||||
id, Cold, tagTime(msg),
|
||||
"\3%02d%s\3\tleaves%s%s",
|
||||
|
@ -333,8 +337,10 @@ static void handleReplyTopic(struct Message *msg) {
|
|||
require(msg, false, 3);
|
||||
if (!replies.topic) return;
|
||||
replies.topic--;
|
||||
size_t id = idFor(msg->params[1]);
|
||||
urlScan(id, NULL, msg->params[2]);
|
||||
uiFormat(
|
||||
idFor(msg->params[1]), Cold, tagTime(msg),
|
||||
id, Cold, tagTime(msg),
|
||||
"The sign in \3%02d%s\3 reads: %s",
|
||||
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) {
|
||||
require(msg, true, 2);
|
||||
size_t id = idFor(msg->params[0]);
|
||||
if (msg->params[1][0]) {
|
||||
urlScan(id, msg->nick, msg->params[1]);
|
||||
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",
|
||||
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0],
|
||||
msg->params[1]
|
||||
);
|
||||
} else {
|
||||
uiFormat(
|
||||
idFor(msg->params[0]), Warm, tagTime(msg),
|
||||
id, Warm, tagTime(msg),
|
||||
"\3%02d%s\3\tremoves the sign in \3%02d%s\3",
|
||||
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 mention = !mine && isMention(msg);
|
||||
if (!notice && !mine) completeTouch(id, msg->nick, hash(msg->user));
|
||||
urlScan(id, msg->nick, msg->params[1]);
|
||||
if (notice) {
|
||||
uiFormat(
|
||||
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