1 /* Copyright (C) 2020  C. McEnroe <june@causal.agency>
2  *
3  * This program is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation, either version 3 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  *
16  * Additional permission under GNU GPL version 3 section 7:
17  *
18  * If you modify this Program, or any covered work, by linking or
19  * combining it with OpenSSL (or a modified version of that library),
20  * containing parts covered by the terms of the OpenSSL License and the
21  * original SSLeay license, the licensors of this Program grant you
22  * additional permission to convey the resulting work. Corresponding
23  * Source for a non-source form of such a combination shall include the
24  * source code for the parts of OpenSSL used as well as that of the
25  * covered work.
26  */
27 
28 #include <assert.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <getopt.h>
32 #include <stdarg.h>
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <sysexits.h>
39 #include <time.h>
40 #include <wchar.h>
41 
42 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
43 #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
44 
45 typedef unsigned uint;
46 typedef unsigned char byte;
47 
48 static inline char *seprintf(char *ptr, char *end, const char *fmt, ...)
49 	__attribute__((format(printf, 3, 4)));
seprintf(char * ptr,char * end,const char * fmt,...)50 static inline char *seprintf(char *ptr, char *end, const char *fmt, ...) {
51 	va_list ap;
52 	va_start(ap, fmt);
53 	int n = vsnprintf(ptr, end - ptr, fmt, ap);
54 	va_end(ap);
55 	if (n < 0) return NULL;
56 	if (n > end - ptr) return end;
57 	return ptr + n;
58 }
59 
60 enum Attr {
61 	BIT(Bold),
62 	BIT(Reverse),
63 	BIT(Italic),
64 	BIT(Underline),
65 };
66 enum Color {
67 	White, Black, Blue, Green, Red, Brown, Magenta, Orange,
68 	Yellow, LightGreen, Cyan, LightCyan, LightBlue, Pink, Gray, LightGray,
69 	Default = 99,
70 	ColorCap,
71 };
72 struct Style {
73 	enum Attr attr;
74 	enum Color fg, bg;
75 };
76 
77 static const struct Style StyleDefault = { 0, Default, Default };
78 enum { B = '\2', C = '\3', O = '\17', R = '\26', I = '\35', U = '\37' };
79 
styleParse(struct Style * style,const char ** str)80 static inline size_t styleParse(struct Style *style, const char **str) {
81 	switch (**str) {
82 		break; case B: (*str)++; style->attr ^= Bold;
83 		break; case O: (*str)++; *style = StyleDefault;
84 		break; case R: (*str)++; style->attr ^= Reverse;
85 		break; case I: (*str)++; style->attr ^= Italic;
86 		break; case U: (*str)++; style->attr ^= Underline;
87 		break; case C: {
88 			(*str)++;
89 			if (!isdigit(**str)) {
90 				style->fg = Default;
91 				style->bg = Default;
92 				break;
93 			}
94 			style->fg = *(*str)++ - '0';
95 			if (isdigit(**str)) style->fg = style->fg * 10 + *(*str)++ - '0';
96 			if ((*str)[0] != ',' || !isdigit((*str)[1])) break;
97 			(*str)++;
98 			style->bg = *(*str)++ - '0';
99 			if (isdigit(**str)) style->bg = style->bg * 10 + *(*str)++ - '0';
100 		}
101 	}
102 	return strcspn(*str, (const char[]) { B, C, O, R, I, U, '\0' });
103 }
104 
styleStrip(char * buf,size_t cap,const char * str)105 static inline void styleStrip(char *buf, size_t cap, const char *str) {
106 	*buf = '\0';
107 	char *ptr = buf, *end = &buf[cap];
108 	struct Style style = StyleDefault;
109 	while (*str) {
110 		size_t len = styleParse(&style, &str);
111 		ptr = seprintf(ptr, end, "%.*s", (int)len, str);
112 		str += len;
113 	}
114 }
115 
116 enum { None, Debug, Network, IDCap = 256 };
117 extern char *idNames[IDCap];
118 extern enum Color idColors[IDCap];
119 extern uint idNext;
120 
idFind(const char * name)121 static inline uint idFind(const char *name) {
122 	for (uint id = 0; id < idNext; ++id) {
123 		if (!strcasecmp(idNames[id], name)) return id;
124 	}
125 	return None;
126 }
127 
idFor(const char * name)128 static inline uint idFor(const char *name) {
129 	uint id = idFind(name);
130 	if (id) return id;
131 	if (idNext == IDCap) return Network;
132 	idNames[idNext] = strdup(name);
133 	idColors[idNext] = Default;
134 	if (!idNames[idNext]) err(EX_OSERR, "strdup");
135 	return idNext++;
136 }
137 
138 extern uint32_t hashInit;
139 extern uint32_t hashBound;
_hash(const char * str)140 static inline uint32_t _hash(const char *str) {
141 	if (*str == '~') str++;
142 	uint32_t hash = hashInit;
143 	for (; *str; ++str) {
144 		hash = (hash << 5) | (hash >> 27);
145 		hash ^= *str;
146 		hash *= 0x27220A95;
147 	}
148 	return hash;
149 }
hash(const char * str)150 static inline enum Color hash(const char *str) {
151 	if (hashBound < Blue) return Default;
152 	return Blue + _hash(str) % (hashBound + 1 - Blue);
153 }
154 
155 extern struct Network {
156 	char *name;
157 	uint userLen;
158 	uint hostLen;
159 	char *chanTypes;
160 	char *statusmsg;
161 	char *prefixes;
162 	char *prefixModes;
163 	char *listModes;
164 	char *paramModes;
165 	char *setParamModes;
166 	char *channelModes;
167 	char excepts;
168 	char invex;
169 } network;
170 
171 #define ENUM_CAP \
172 	X("causal.agency/consumer", CapConsumer) \
173 	X("chghost", CapChghost) \
174 	X("extended-join", CapExtendedJoin) \
175 	X("invite-notify", CapInviteNotify) \
176 	X("message-tags", CapMessageTags) \
177 	X("multi-prefix", CapMultiPrefix) \
178 	X("sasl", CapSASL) \
179 	X("server-time", CapServerTime) \
180 	X("setname", CapSetname) \
181 	X("userhost-in-names", CapUserhostInNames) \
182 	X("znc.in/self-message", CapSelfMessage)
183 
184 enum Cap {
185 #define X(name, id) BIT(id),
186 	ENUM_CAP
187 #undef X
188 };
189 
190 extern struct Self {
191 	bool debug;
192 	bool kiosk;
193 	bool restricted;
194 	size_t pos;
195 	enum Cap caps;
196 	char *plain;
197 	char *mode;
198 	char *join;
199 	char *nick;
200 	char *user;
201 	char *host;
202 	enum Color color;
203 	char *invited;
204 	char *quit;
205 } self;
206 
set(char ** field,const char * value)207 static inline void set(char **field, const char *value) {
208 	free(*field);
209 	*field = strdup(value);
210 	if (!*field) err(EX_OSERR, "strdup");
211 }
212 
213 #define ENUM_TAG \
214 	X("+draft/reply", TagReply) \
215 	X("causal.agency/pos", TagPos) \
216 	X("msgid", TagMsgID) \
217 	X("time", TagTime)
218 
219 enum Tag {
220 #define X(name, id) id,
221 	ENUM_TAG
222 #undef X
223 	TagCap,
224 };
225 
226 enum { ParamCap = 254 };
227 struct Message {
228 	char *tags[TagCap];
229 	char *nick;
230 	char *user;
231 	char *host;
232 	char *cmd;
233 	char *params[ParamCap];
234 };
235 
236 void ircConfig(
237 	bool insecure, const char *trust, const char *cert, const char *priv
238 );
239 int ircConnect(const char *bind, const char *host, const char *port);
240 void ircHandshake(void);
241 void ircPrintCert(void);
242 void ircRecv(void);
243 void ircSend(const char *ptr, size_t len);
244 void ircFormat(const char *format, ...)
245 	__attribute__((format(printf, 1, 2)));
246 void ircClose(void);
247 
248 extern uint execID;
249 extern int execPipe[2];
250 extern int utilPipe[2];
251 
252 enum { UtilCap = 16 };
253 struct Util {
254 	uint argc;
255 	const char *argv[UtilCap];
256 };
257 
utilPush(struct Util * util,const char * arg)258 static inline void utilPush(struct Util *util, const char *arg) {
259 	if (1 + util->argc < UtilCap) {
260 		util->argv[util->argc++] = arg;
261 	} else {
262 		errx(EX_CONFIG, "too many utility arguments");
263 	}
264 }
265 
266 enum Reply {
267 	ReplyAway = 1,
268 	ReplyBan,
269 	ReplyExcepts,
270 	ReplyHelp,
271 	ReplyInvex,
272 	ReplyJoin,
273 	ReplyList,
274 	ReplyMode,
275 	ReplyNames,
276 	ReplyNamesAuto,
277 	ReplyTopic,
278 	ReplyTopicAuto,
279 	ReplyWho,
280 	ReplyWhois,
281 	ReplyWhowas,
282 	ReplyCap,
283 };
284 
285 extern uint replies[ReplyCap];
286 
287 void handle(struct Message *msg);
288 void command(uint id, char *input);
289 const char *commandIsPrivmsg(uint id, const char *input);
290 const char *commandIsNotice(uint id, const char *input);
291 const char *commandIsAction(uint id, const char *input);
292 size_t commandWillSplit(uint id, const char *input);
293 void commandCompleteAdd(void);
294 
295 enum Heat { Ice, Cold, Warm, Hot };
296 enum { TimeCap = 64 };
297 extern enum Heat uiThreshold;
298 extern struct Time {
299 	bool enable;
300 	const char *format;
301 	int width;
302 } uiTime;
303 extern struct Util uiNotifyUtil;
304 void uiInitEarly(void);
305 void uiInitLate(void);
306 void uiShow(void);
307 void uiHide(void);
308 void uiDraw(void);
309 void uiWindows(void);
310 void uiShowID(uint id);
311 void uiShowNum(uint num);
312 void uiMoveID(uint id, uint num);
313 void uiCloseID(uint id);
314 void uiCloseNum(uint id);
315 void uiRead(void);
316 void uiWrite(uint id, enum Heat heat, const time_t *time, const char *str);
317 void uiFormat(
318 	uint id, enum Heat heat, const time_t *time, const char *format, ...
319 ) __attribute__((format(printf, 4, 5)));
320 void uiLoad(const char *name);
321 int uiSave(void);
322 
323 enum { BufferCap = 1024 };
324 struct Buffer;
325 struct Line {
326 	uint num;
327 	enum Heat heat;
328 	time_t time;
329 	char *str;
330 };
331 struct Buffer *bufferAlloc(void);
332 void bufferFree(struct Buffer *buffer);
333 const struct Line *bufferSoft(const struct Buffer *buffer, size_t i);
334 const struct Line *bufferHard(const struct Buffer *buffer, size_t i);
335 int bufferPush(
336 	struct Buffer *buffer, int cols, enum Heat thresh,
337 	enum Heat heat, time_t time, const char *str
338 );
339 int bufferReflow(
340 	struct Buffer *buffer, int cols, enum Heat thresh, size_t tail
341 );
342 
343 enum Edit {
344 	EditHead,
345 	EditTail,
346 	EditPrev,
347 	EditNext,
348 	EditPrevWord,
349 	EditNextWord,
350 	EditDeleteHead,
351 	EditDeleteTail,
352 	EditDeletePrev,
353 	EditDeleteNext,
354 	EditDeletePrevWord,
355 	EditDeleteNextWord,
356 	EditPaste,
357 	EditTranspose,
358 	EditCollapse,
359 	EditInsert,
360 	EditComplete,
361 	EditExpand,
362 	EditEnter,
363 };
364 void edit(uint id, enum Edit op, wchar_t ch);
365 char *editBuffer(size_t *pos);
366 void editCompleteAdd(void);
367 
368 const char *complete(uint id, const char *prefix);
369 const char *completeSubstr(uint id, const char *substr);
370 void completeAccept(void);
371 void completeReject(void);
372 void completeAdd(uint id, const char *str, enum Color color);
373 void completeTouch(uint id, const char *str, enum Color color);
374 void completeReplace(uint id, const char *old, const char *new);
375 void completeRemove(uint id, const char *str);
376 void completeClear(uint id);
377 uint completeID(const char *str);
378 enum Color completeColor(uint id, const char *str);
379 
380 extern struct Util urlOpenUtil;
381 extern struct Util urlCopyUtil;
382 void urlScan(uint id, const char *nick, const char *mesg);
383 void urlOpenCount(uint id, uint count);
384 void urlOpenMatch(uint id, const char *str);
385 void urlCopyMatch(uint id, const char *str);
386 int urlSave(FILE *file);
387 void urlLoad(FILE *file, size_t version);
388 
389 enum { FilterCap = 64 };
390 extern struct Filter {
391 	enum Heat heat;
392 	char *mask;
393 	char *cmd;
394 	char *chan;
395 	char *mesg;
396 } filters[FilterCap];
397 struct Filter filterParse(enum Heat heat, char *pattern);
398 struct Filter filterAdd(enum Heat heat, const char *pattern);
399 bool filterRemove(struct Filter filter);
400 enum Heat filterCheck(enum Heat heat, uint id, const struct Message *msg);
401 
402 void logOpen(void);
403 void logFormat(uint id, const time_t *time, const char *format, ...)
404 	__attribute__((format(printf, 3, 4)));
405 void logClose(void);
406 
407 char *configPath(char *buf, size_t cap, const char *path, int i);
408 char *dataPath(char *buf, size_t cap, const char *path, int i);
409 FILE *configOpen(const char *path, const char *mode);
410 FILE *dataOpen(const char *path, const char *mode);
411 
412 int getopt_config(
413 	int argc, char *const *argv,
414 	const char *optstring, const struct option *longopts, int *longindex
415 );
416