242 lines
6.0 KiB
C
242 lines
6.0 KiB
C
/* 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 <getopt.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <sysexits.h>
|
|
#include <time.h>
|
|
#include <wchar.h>
|
|
|
|
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
|
#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
|
|
|
|
#define XDG_SUBDIR "catgirl"
|
|
|
|
typedef unsigned char byte;
|
|
|
|
int procPipe[2];
|
|
|
|
enum Color {
|
|
White, Black, Blue, Green, Red, Brown, Magenta, Orange,
|
|
Yellow, LightGreen, Cyan, LightCyan, LightBlue, Pink, Gray, LightGray,
|
|
Default = 99,
|
|
};
|
|
|
|
enum { None, Debug, Network, IDCap = 256 };
|
|
extern char *idNames[IDCap];
|
|
extern enum Color idColors[IDCap];
|
|
extern size_t idNext;
|
|
|
|
static inline size_t idFind(const char *name) {
|
|
for (size_t id = 0; id < idNext; ++id) {
|
|
if (!strcmp(idNames[id], name)) return id;
|
|
}
|
|
return None;
|
|
}
|
|
|
|
static inline size_t idFor(const char *name) {
|
|
size_t id = idFind(name);
|
|
if (id) return id;
|
|
if (idNext == IDCap) return Network;
|
|
idNames[idNext] = strdup(name);
|
|
if (!idNames[idNext]) err(EX_OSERR, "strdup");
|
|
idColors[idNext] = Default;
|
|
return idNext++;
|
|
}
|
|
|
|
#define ENUM_CAP \
|
|
X("extended-join", CapExtendedJoin) \
|
|
X("sasl", CapSASL) \
|
|
X("server-time", CapServerTime) \
|
|
X("userhost-in-names", CapUserhostInNames)
|
|
|
|
enum Cap {
|
|
#define X(name, id) BIT(id),
|
|
ENUM_CAP
|
|
#undef X
|
|
};
|
|
|
|
extern struct Self {
|
|
bool debug;
|
|
char *plain;
|
|
const char *join;
|
|
enum Cap caps;
|
|
char *network;
|
|
char *chanTypes;
|
|
char *prefixes;
|
|
char *nick;
|
|
char *user;
|
|
enum Color color;
|
|
char *quit;
|
|
} self;
|
|
|
|
static inline void set(char **field, const char *value) {
|
|
free(*field);
|
|
*field = strdup(value);
|
|
if (!*field) err(EX_OSERR, "strdup");
|
|
}
|
|
|
|
#define ENUM_TAG \
|
|
X("time", TagTime)
|
|
|
|
enum Tag {
|
|
#define X(name, id) id,
|
|
ENUM_TAG
|
|
#undef X
|
|
TagCap,
|
|
};
|
|
|
|
enum { ParamCap = 15 };
|
|
struct Message {
|
|
char *tags[TagCap];
|
|
char *nick;
|
|
char *user;
|
|
char *host;
|
|
char *cmd;
|
|
char *params[ParamCap];
|
|
};
|
|
|
|
void ircConfig(bool insecure, FILE *cert, FILE *priv);
|
|
int ircConnect(const char *host, const char *port);
|
|
void ircRecv(void);
|
|
void ircSend(const char *ptr, size_t len);
|
|
void ircFormat(const char *format, ...)
|
|
__attribute__((format(printf, 1, 2)));
|
|
|
|
extern struct Replies {
|
|
size_t join;
|
|
size_t topic;
|
|
size_t names;
|
|
size_t whois;
|
|
} replies;
|
|
|
|
void handle(struct Message msg);
|
|
void command(size_t id, char *input);
|
|
const char *commandIsPrivmsg(size_t id, const char *input);
|
|
const char *commandIsNotice(size_t id, const char *input);
|
|
const char *commandIsAction(size_t id, const char *input);
|
|
void commandComplete(void);
|
|
|
|
enum Heat { Cold, Warm, Hot };
|
|
void uiInit(void);
|
|
void uiShow(void);
|
|
void uiHide(void);
|
|
void uiDraw(void);
|
|
void uiShowID(size_t id);
|
|
void uiShowNum(size_t num);
|
|
void uiCloseID(size_t id);
|
|
void uiCloseNum(size_t id);
|
|
void uiRead(void);
|
|
void uiWrite(size_t id, enum Heat heat, const time_t *time, const char *str);
|
|
void uiFormat(
|
|
size_t id, enum Heat heat, const time_t *time, const char *format, ...
|
|
) __attribute__((format(printf, 4, 5)));
|
|
void uiLoad(const char *name);
|
|
int uiSave(const char *name);
|
|
|
|
enum Edit {
|
|
EditHead,
|
|
EditTail,
|
|
EditPrev,
|
|
EditNext,
|
|
EditPrevWord,
|
|
EditNextWord,
|
|
EditDeleteHead,
|
|
EditDeleteTail,
|
|
EditDeletePrev,
|
|
EditDeleteNext,
|
|
EditDeletePrevWord,
|
|
EditDeleteNextWord,
|
|
EditPaste,
|
|
EditInsert,
|
|
EditComplete,
|
|
EditEnter,
|
|
};
|
|
void edit(size_t id, enum Edit op, wchar_t ch);
|
|
char *editBuffer(size_t *pos);
|
|
|
|
const char *complete(size_t id, const char *prefix);
|
|
void completeAccept(void);
|
|
void completeReject(void);
|
|
void completeAdd(size_t id, const char *str, enum Color color);
|
|
void completeTouch(size_t id, const char *str, enum Color color);
|
|
void completeReplace(size_t id, const char *old, const char *new);
|
|
void completeRemove(size_t id, const char *str);
|
|
void completeClear(size_t id);
|
|
size_t completeID(const char *str);
|
|
enum Color completeColor(size_t id, const char *str);
|
|
|
|
extern const char *urlOpenUtil;
|
|
extern const char *urlCopyUtil;
|
|
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);
|
|
void urlCopyMatch(size_t id, const char *str);
|
|
|
|
FILE *configOpen(const char *path, const char *mode);
|
|
FILE *dataOpen(const char *path, const char *mode);
|
|
|
|
int getopt_config(
|
|
int argc, char *const *argv,
|
|
const char *optstring, const struct option *longopts, int *longindex
|
|
);
|
|
|
|
extern uint32_t hashInit;
|
|
static inline enum Color hash(const char *str) {
|
|
if (*str == '~') str++;
|
|
uint32_t hash = hashInit;
|
|
for (; *str; ++str) {
|
|
hash = (hash << 5) | (hash >> 27);
|
|
hash ^= *str;
|
|
hash *= 0x27220A95;
|
|
}
|
|
return 2 + hash % 74;
|
|
}
|
|
|
|
#define BASE64_SIZE(len) (1 + ((len) + 2) / 3 * 4)
|
|
static const char Base64[64] = {
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
};
|
|
static inline void base64(char *dst, const byte *src, size_t len) {
|
|
size_t i = 0;
|
|
while (len > 2) {
|
|
dst[i++] = Base64[0x3F & (src[0] >> 2)];
|
|
dst[i++] = Base64[0x3F & (src[0] << 4 | src[1] >> 4)];
|
|
dst[i++] = Base64[0x3F & (src[1] << 2 | src[2] >> 6)];
|
|
dst[i++] = Base64[0x3F & src[2]];
|
|
src += 3;
|
|
len -= 3;
|
|
}
|
|
if (len) {
|
|
dst[i++] = Base64[0x3F & (src[0] >> 2)];
|
|
if (len > 1) {
|
|
dst[i++] = Base64[0x3F & (src[0] << 4 | src[1] >> 4)];
|
|
dst[i++] = Base64[0x3F & (src[1] << 2)];
|
|
} else {
|
|
dst[i++] = Base64[0x3F & (src[0] << 4)];
|
|
dst[i++] = '=';
|
|
}
|
|
dst[i++] = '=';
|
|
}
|
|
dst[i] = '\0';
|
|
}
|
|
|
|
// Defined in libcrypto if missing from libc:
|
|
void explicit_bzero(void *b, size_t len);
|