summary refs log blame commit diff
path: root/bounce.h
blob: d5f4170d74f26f62299900726c4a3b4032a6da21 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                                                       
                                                                       




                                                                    
                                               
  
                                                                         
   
                    
                  
                   
                   
                     
                
                   
                                                 
      


                           
                                                     
                            
                                                        
                                               
                           
                                 
                       
                
                   

                               
  
                                                
                                                              
                                                                
                                                       
                                                 
                              
                                                   
         
                   
 
                                               
                                         
                                         
                              
                                       
                                                  
                                                
                                  
                                             
                                             
                                                   
                                           
                                           
                            
                                         
                                  
                                                    




                             

                               
                          
                             

                                    






                                     
                                                                                

                                              
                                                 


                                                                      
                                                                                   

                              
                                           



                                  
                                                                               




                                                                   


                                                                     



                   
                    
 
                           
                                      
                                
                                 
                                                               
                    
                          
 
                                                                 

                                                                            
 
                                                                     
                                                                            
                      

                                              
 
                     
                        
                                            
                                       
                                              
                                       
                                                                    
                                                                 
                                               
                                          
 
                          
                
                                                       
                                                            

                                      
                            
                            




                                                                            



                                                                          
                                                  






















                                                                              
/* Copyright (C) 2019  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 <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <tls.h>

#include "compat.h"

#ifndef CERTBOT_PATH
#define CERTBOT_PATH "/usr/local/etc/letsencrypt"
#endif

#ifndef LIBRESSL_BIN_PREFIX
#define LIBRESSL_BIN_PREFIX
#endif

#define SOURCE_URL "https://git.causal.agency/pounce"
#define ORIGIN "irc.invalid"

#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))

typedef unsigned char byte;

enum { MessageCap = 8191 + 512 };

enum { ParamCap = 15 };
struct Message {
	char *tags;
	char *origin;
	char *cmd;
	char *params[ParamCap];
};

static inline struct Message parse(char *line) {
	struct Message msg = {0};
	if (line[0] == '@') msg.tags = 1 + strsep(&line, " ");
	if (line[0] == ':') msg.origin = 1 + strsep(&line, " ");
	msg.cmd = strsep(&line, " ");
	for (size_t i = 0; line && i < ParamCap; ++i) {
		if (line[0] == ':') {
			msg.params[i] = &line[1];
			break;
		}
		msg.params[i] = strsep(&line, " ");
	}
	return msg;
}

#define ENUM_CAP \
	X("account-notify", CapAccountNotify) \
	X("account-tag", CapAccountTag) \
	X("away-notify", CapAwayNotify) \
	X("batch", CapBatch) \
	X("cap-notify", CapCapNotify) \
	X("causal.agency/consumer", CapConsumer) \
	X("causal.agency/passive", CapPassive) \
	X("chghost", CapChghost) \
	X("extended-join", CapExtendedJoin) \
	X("invite-notify", CapInviteNotify) \
	X("labeled-response", CapLabeledResponse) \
	X("message-tags", CapMessageTags) \
	X("multi-prefix", CapMultiPrefix) \
	X("sasl", CapSASL) \
	X("server-time", CapServerTime) \
	X("setname", CapSetname) \
	X("userhost-in-names", CapUserhostInNames) \
	X("", CapUnsupported)

enum Cap {
#define X(name, id) BIT(id),
	ENUM_CAP
#undef X
	CapBits,
	TagCaps = 0
		| CapAccountTag
		| CapBatch
		| CapConsumer
		| CapLabeledResponse
		| CapMessageTags
		| CapServerTime,
};

static const char *CapNames[] = {
#define X(name, id) [id##Bit] = name,
	ENUM_CAP
#undef X
};

static inline enum Cap capParse(const char *list, const char *values[CapBits]) {
	enum Cap caps = 0;
	while (*list) {
		enum Cap cap = CapUnsupported;
		size_t len = strcspn(list, "= ");
		for (size_t i = 0; i < ARRAY_LEN(CapNames); ++i) {
			if (len != strlen(CapNames[i])) continue;
			if (strncmp(list, CapNames[i], len)) continue;
			cap = 1 << i;
			if (list[len] == '=' && values) values[i] = &list[len + 1];
			break;
		}
		caps |= cap;
		list += strcspn(list, " ");
		if (*list) list++;
	}
	return caps;
}

static inline const char *capList(enum Cap caps, const char *values[CapBits]) {
	static char buf[1024];
	buf[0] = '\0';
	for (size_t i = 0; i < ARRAY_LEN(CapNames); ++i) {
		if (caps & (1 << i)) {
			if (buf[0]) strlcat(buf, " ", sizeof(buf));
			strlcat(buf, CapNames[i], sizeof(buf));
			if (values && values[i]) {
				strlcat(buf, "=", sizeof(buf));
				strlcat(buf, values[i], sizeof(buf));
			}
		}
	}
	return buf;
}

extern bool verbose;

void ringAlloc(size_t len);
void ringProduce(const char *line);
size_t ringConsumer(const char *name);
size_t ringPos(size_t consumer);
size_t ringDiff(size_t consumer);
const char *ringPeek(struct timeval *time, size_t consumer);
const char *ringConsume(struct timeval *time, size_t consumer);
void ringInfo(void);
int ringSave(FILE *file);
void ringLoad(FILE *file);

void localConfig(FILE *cert, FILE *priv, FILE *ca, bool require);
size_t localBind(int fds[], size_t cap, const char *host, const char *port);
size_t localUnix(int fds[], size_t cap, const char *path);
struct tls *localAccept(int *fd, int bind);

void serverConfig(bool insecure, const char *cert, const char *priv);
int serverConnect(const char *bindHost, const char *host, const char *port);
void serverRecv(void);
void serverSend(const char *ptr, size_t len);
void serverFormat(const char *format, ...)
	__attribute__((format(printf, 1, 2)));

extern bool clientCA;
extern char *clientPass;
extern char *clientAway;
struct Client *clientAlloc(struct tls *tls);
void clientFree(struct Client *client);
bool clientError(const struct Client *client);
void clientRecv(struct Client *client);
void clientSend(struct Client *client, const char *ptr, size_t len);
void clientFormat(struct Client *client, const char *format, ...)
	__attribute__((format(printf, 2, 3)));
size_t clientDiff(const struct Client *client);
void clientConsume(struct Client *client);

extern bool stateNoNames;
extern enum Cap stateCaps;
void stateLogin(
	const char *pass, bool sasl, const char *plain,
	const char *nick, const char *user, const char *real
);
bool stateReady(void);
void stateParse(char *line);
void stateSync(struct Client *client);
const char *stateNick(void);
const char *stateEcho(void);

struct option;
int getopt_config(
	int argc, char *const *argv,
	const char *optstring, const struct option *longopts, int *longindex
);

static const char Base64[64] = {
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
};

#define BASE64_SIZE(len) (1 + ((len) + 2) / 3 * 4)

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';
}