fteqw/plugins/jabber/xmpp.h

360 lines
12 KiB
C
Raw Normal View History

#include "../plugin.h"
#include "xml.h"
//configuration
//#define NOICE //if defined, we only do simple raw udp connections.
#define FILETRANSFERS //IBB only, speeds suck. autoaccept is forced on. no protection from mods stuffcmding sendfile commands. needs more extensive testing
#define QUAKECONNECT //including quake ICE connections (depends upon jingle)
#define VOIP //enables voice chat (depends upon jingle)
//#define VOIP_LEGACY //enables google-only voice chat compat. google have not kept up with the standardisation of jingle (aka: gingle).
//#define VOIP_LEGACY_ONLY //disables jingle feature (advert+detection only)
#define JINGLE //enables jingle signalling
#ifdef JINGLE
#include "../../engine/common/netinc.h"
#else
#undef VOIP
#undef VOIP_LEGACY
#endif
#define JCL_BUILD "5"
//#define DEFAULTDOMAIN "triptohell.info"
#define DEFAULTRESOURCE "Quake"
#define QUAKEMEDIAXMLNS "http://fteqw.com/protocol/quake"
#define DISCONODE "http://fteqw.com/ftexmpp" //some sort of client identifier
#define DEFAULTICEMODE ICEM_ICE
#define MEDIATYPE_QUAKE "quake"
#define MEDIATYPE_VIDEO "video"
#define MEDIATYPE_AUDIO "audio"
#define JCL_MAXMSGLEN 0x10000
//values are not on the wire or anything
#define CAP_VOICE (1u<<0) //supports voice
#define CAP_GOOGLE_VOICE (1u<<1) //google supports some old non-standard version of jingle.
#define CAP_VIDEO (1u<<2) //supports video calls
#define CAP_GAMEINVITE (1u<<3) //supports game invites. custom/private protocol
#define CAP_POKE (1u<<4) //can be slapped.
#define CAP_SIFT (1u<<5) //non-jingle file transfers
#define CAP_AVATARS (1u<<6) //can enable querying for user avatars, but cannot disable advertising our own.
//not actually capabilities, but just down to how we handle querying them.
#define CAP_QUERYING (1u<<29) //we've sent a query and are waiting for the response.
#define CAP_QUERIED (1u<<30) //feature capabilities are actually know.
#define CAP_QUERYFAILED (1u<<31) //caps request failed due to bad hash or some such.
//features that default to disabled.
#define CAP_DEFAULTENABLEDCAPS (CAP_VOICE/*|CAP_VIDEO*/|CAP_GAMEINVITE|CAP_POKE|CAP_AVATARS/*|CAP_SIFT*/|CAP_GOOGLE_VOICE)
typedef struct bresource_s
{
char bstatus[128]; //basic status
char fstatus[128]; //full status
char server[256];
int servertype; //0=none, 1=already a client, 2=joinable
int priority;
unsigned int buggycaps;
unsigned int caps;
char *client_node; //vendor name
char *client_ver; //cap hash value
char *client_hash; //cap hash method
char *client_ext; //deprecated. additionally queried
struct bresource_s *next;
char resource[1];
} bresource_t;
typedef struct buddy_s
{
bresource_t *resources;
bresource_t *defaultresource; //this is the one that last replied (must be null for chatrooms, so we only talk to the room in general)
bresource_t *ourselves; //this is set back to ourselves when in a chatroom
int defaulttimestamp;
qboolean askfriend;
qboolean friended;
qboolean vcardphotochanged;
enum {
BT_UNKNOWN, //this buddy isn't known...
BT_ROSTER, //this is a friend! or at least on our list of potential friends anyway.
BT_CHATROOM //we're treating this 'guy' as a MUC, each of their resources is a different person. which is weird.
} btype;
qboolean room_autojoin;
char *room_nick;
char *room_password;
char *room_topic;
char name[256];
char vcardphotohash[41];
qhandle_t image;
struct buddy_s *next;
char accountdomain[1]; //no resource on there
} buddy_t;
typedef struct jclient_s
{
int accountnum; //a private id to track which client links are associated with
char redirserveraddr[64]; //if empty, do an srv lookup.
enum
{
JCL_INACTIVE, //not trying to connect.
JCL_DEAD, //not connected. connection died or something.
JCL_AUTHING, //connected, but not able to send any info on it other than to auth
JCL_ACTIVE //we're connected, we got a buddy list and everything
} status;
unsigned int timeout; //reconnect/ping timer
char errormsg[256];
unsigned int enabledcapabilities;
qhandle_t socket;
qhandle_t rcon_pipe; //contains console prints
char rcon_peer[256]; //the name of the guy currently receiving console prints
//we buffer output for times when the outgoing socket is full.
//mostly this only happens at the start of the connection when the socket isn't actually open yet.
char *outbuf;
int outbufpos;
int outbuflen;
int outbufmax;
char bufferedinmessage[JCL_MAXMSGLEN+1]; //servers are required to be able to handle messages no shorter than a specific size.
//which means we need to be able to handle messages when they get to us.
//servers can still handle larger messages if they choose, so this might not be enough.
int bufferedinammount;
char defaultdest[256];
//config info
char serveraddr[64]; //if empty, do an srv lookup.
int serverport;
char domain[256];
char username[256];
char resource[256];
char certificatedomain[256];
int forcetls; //-1=off, 0=ifpossible, 1=fail if can't upgrade, 2=old-style tls
qboolean savepassword;
char fulljid[256]; //this is our full username@domain/resource string
char barejid[256]; //this is our bare username@domain string
char localalias[256];//this is what's shown infront of outgoing messages. >> by default until we can get our name.
char vcardphotohash[20]; //20-byte sha1 hash.
enum
{
VCP_UNKNOWN,
VCP_NONE,
VCP_KNOWN
} vcardphotohashstatus;
qboolean vcardphotohashchanged; //forces a presence send.
int instreampos;
qboolean connecting; //still waiting for intial stream tag
qboolean connected; //fully on server and authed and everything.
qboolean issecure; //tls enabled (either upgraded or initially)
int streamdebug; //echo the stream to subconsoles
qboolean preapproval; //server supports presence preapproval
char curquakeserver[2048];
char defaultnamespace[2048]; //should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs)
struct sasl_ctx_s
{
char *username; //might be different from the account name, but probably isn't.
char *domain; //might be different from the account domain, but probably isn't.
qboolean issecure; //tls enabled (either upgraded or initially)
int socket;
//this stuff should be saved
char password_plain[256]; //plain password. scrubbed if we auth using a hashed auth.
qbyte password_hash[256]; //safer password, not encrypted, but stored hashed.
size_t password_hash_size;
char password_validity[256]; //internal string used to check that the salt was unchanged
struct saslmethod_s *authmethod; //null name = oauth2->saslmethod
qboolean allowauth_plainnontls; //allow plain plain
qboolean allowauth_plaintls; //allow tls plain
qboolean allowauth_digestmd5; //allow digest-md5 auth
qboolean allowauth_scramsha1; //allow scram-sha-1 auth
qboolean allowauth_oauth2; //use oauth2 where possible
struct
{
char authnonce[256];
} digest;
struct
{
qboolean plus;
char authnonce[256];
char authvhash[20];
char authcbindtype[20];
char authcbinding[256];
hashfunc_t *hashfunc;
size_t hashsize;
} scram;
struct
{
char saslmethod[64];
char obtainurl[256];
char refreshurl[256];
char clientid[256];
char clientsecret[256];
char *useraccount;
char *scope;
char *accesstoken; //one-shot access token
char *refreshtoken; //long-term token that we can use to get new access tokens
char *authtoken; //short-term authorisation token, usable to get an access token (and a refresh token if we're lucky)
} oauth2;
} sasl;
struct iq_s
{
struct iq_s *next;
char id[64];
int timeout;
qboolean (*callback) (struct jclient_s *jcl, struct subtree_s *tree, struct iq_s *iq);
void *usrptr;
char to[1];
} *pendingiqs;
#ifdef JINGLE
struct c2c_s
{
struct c2c_s *next;
qboolean accepted; //connection is going
qboolean creator; //true if we're the creator.
unsigned int peercaps;
qboolean displayed; //temp flag for displaying jingle sessions with people that are not on our buddy list for whatever reasons
struct
{
char name[64]; //uniquely identifies the content within the session.
enum iceproto_e mediatype;
enum icemode_e method; //ICE_RAW or ICE_ICE. this is what the peer asked for. updated if we degrade it.
struct icestate_s *ice;
char *peeraddr;
int peerport;
} content[3];
int contents;
char *with;
char sid[1];
} *c2c;
#endif
#ifdef FILETRANSFERS
struct ft_s
{
struct ft_s *next;
char fname[MAX_QPATH];
int size;
int sizedone;
char *with;
char md5hash[16];
int privateid;
char iqid[64];
char sid[64];
int blocksize;
unsigned short seq;
qhandle_t file;
qhandle_t stream;
qboolean begun; //handshake
qboolean eof;
qboolean transmitting; //we're offering
qboolean allowed; //if false, don't handshake the transfer
struct
{
char jid[128];
char host[40];
int port;
} streamhosts[16];
int nexthost;
enum
{
STRM_IDLE,
STRM_AUTH,
STRM_AUTHED,
STRM_ACTIVE
} streamstatus;
char indata[64]; //only for handshake data
int inlen;
enum
{
FT_NOTSTARTED,
FT_IBB, //in-band bytestreams
FT_BYTESTREAM //aka: relay
} method;
} *ft;
int privateidseq;
#endif
//persistant achived info about buddies, to avoid spamming the server every time we connect
//such things might result in lag mid game!
struct buddyinfo_s
{
struct buddyinfo_s *next;
char *image;
char *imagehash;
char *imagemime;
char accountdomain[1];
} *buddyinfo;
buddy_t *buddies;
struct iq_s *avatarupdate; //we only grab one buddy's photo at a time, this is to avoid too much spam.
} jclient_t;
#ifdef JINGLE
extern icefuncs_t *piceapi;
#endif
qboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen);
//xmpp functionality
struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode);
void JCL_AddClientMessagef(jclient_t *jcl, const char *fmt, ...);
void JCL_AddClientMessageString(jclient_t *jcl, const char *msg);
qboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create);
void JCL_ForgetBuddy(jclient_t *jcl, buddy_t *buddy, bresource_t *bres);
//quake functionality
void JCL_GenLink(jclient_t *jcl, char *out, int outlen, const char *action, const char *context, const char *contextres, const char *sid, const char *txtfmt, ...);
void Con_SubPrintf(const char *subname, const char *format, ...);
void XMPP_ConversationPrintf(const char *context, const char *title, char *format, ...);
//jingle functions
void JCL_Join(jclient_t *jcl, const char *target, const char *sid, qboolean allow, int protocol);
void JCL_JingleTimeouts(jclient_t *jcl, qboolean killall);
//jingle iq message handlers
qboolean JCL_HandleGoogleSession(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id);
qboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id);
void XMPP_FT_AcceptFile(jclient_t *jcl, int fileid, qboolean accept);
qboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq);
qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, const char *iqfrom, const char *iqid, xmltree_t *tree);
void XMPP_FT_SendFile(jclient_t *jcl, const char *console, const char *to, const char *fname);
void XMPP_FT_Frame(jclient_t *jcl);
void Base64_Add(const char *s, int len);
char *Base64_Finish(void);
int Base64_Decode(char *out, int outlen, const char *src, int srclen);