mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 22:01:50 +00:00
0c8ad17f7c
Added sv_guidkey cvar, allowing cross-server guid key generation (although it lacks auth). Support .ico, because we can. preliminary support for sdl 2.0.6's vulkan stuff. will wait till its actually released before its properly used. Fix capturedemo. videomap should typically use premultiplied alpha, apparently. Updated sound drivers. No more old drivers. Better cvar registration. More drivers optionally support float output. Added certificate log for dtls connections. Rewrote font char cache, now supports full unicode char range, not just ucs-2. Attempt to support FreeType 2.5+ rgba fonts. XMPP now supports carbons, and shows avatars in conversations. Updated xmpp's scram auth to be more strict, including the plus variation (hopefully), to block evil tls proxies. ffmpeg plugin now uses the decoupled api for decoding too. Cef plugin updated to support fte-scheme post data properly, as well as request/response headers (like cross-origin). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5148 fc73d0e0-1445-4013-8a0c-d673dee63da5
608 lines
17 KiB
C
608 lines
17 KiB
C
#include "xmpp.h"
|
|
|
|
#ifdef FILETRANSFERS
|
|
|
|
void XMPP_FT_Frame(jclient_t *jcl)
|
|
{
|
|
static char *hex = "0123456789abcdef";
|
|
char digest[20];
|
|
char domain[sizeof(digest)*2+1];
|
|
int j;
|
|
char *req;
|
|
struct ft_s *ft;
|
|
for (ft = jcl->ft; ft; ft = ft->next)
|
|
{
|
|
if (ft->streamstatus == STRM_IDLE)
|
|
continue;
|
|
|
|
if (ft->stream < 0)
|
|
{
|
|
if (ft->nexthost > 0)
|
|
{
|
|
ft->nexthost--;
|
|
ft->stream = pNet_TCPConnect(ft->streamhosts[ft->nexthost].host, ft->streamhosts[ft->nexthost].port);
|
|
if (ft->stream == -1)
|
|
continue;
|
|
|
|
ft->streamstatus = STRM_AUTH;
|
|
|
|
//'authenticate' with socks5 proxy. tell it that we only support 'authless'.
|
|
pNet_Send(ft->stream, "\x05\x01\x00", 3);
|
|
}
|
|
else
|
|
{
|
|
JCL_AddClientMessagef(jcl,
|
|
"<iq id='%s' to='%s' type='error'>"
|
|
"<error type='cancel'>"
|
|
"<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
|
|
"</error>"
|
|
"</iq>", ft->iqid, ft->with);
|
|
ft->streamstatus = STRM_IDLE;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
char data[8192];
|
|
int len;
|
|
if (ft->streamstatus == STRM_ACTIVE)
|
|
{
|
|
len = pNet_Recv(ft->stream, data, sizeof(data)-1);
|
|
if (len > 0)
|
|
pFS_Write(ft->file, data, len);
|
|
}
|
|
else
|
|
{
|
|
len = pNet_Recv(ft->stream, data, sizeof(data)-1);
|
|
if (len > 0)
|
|
{
|
|
if (ft->streamstatus == STRM_AUTH)
|
|
{
|
|
if (len == 2 && data[0] == 0x05 && data[1] == 0x00)
|
|
{
|
|
//server has accepted us, woo.
|
|
//sid+requester(them)+target(us)
|
|
req = va("%s%s%s", ft->sid, ft->with, jcl->fulljid);
|
|
SHA1(digest, sizeof(digest), req, strlen(req));
|
|
//in hex
|
|
for (req = domain, j=0; j < 20; j++)
|
|
{
|
|
*req++ = hex[(digest[j]>>4) & 0xf];
|
|
*req++ = hex[(digest[j]>>0) & 0xf];
|
|
}
|
|
*req = 0;
|
|
|
|
//connect with hostname(3).
|
|
req = va("\x05\x01%c\x03""%c%s%c%c", 0, (int)strlen(domain), domain, 0, 0);
|
|
pNet_Send(ft->stream, req, strlen(domain)+7);
|
|
ft->streamstatus = STRM_AUTHED;
|
|
}
|
|
else
|
|
len = -1;
|
|
}
|
|
else if (ft->streamstatus == STRM_AUTHED)
|
|
{
|
|
if (data[0] == 0x05 && data[1] == 0x00)
|
|
{
|
|
if (pFS_Open(ft->fname, &ft->file, 2) < 0)
|
|
{
|
|
len = -1;
|
|
JCL_AddClientMessagef(jcl, "<iq id='%s' to='%s' type='error'/>", ft->iqid, ft->with);
|
|
}
|
|
else
|
|
{
|
|
ft->streamstatus = STRM_ACTIVE;
|
|
JCL_AddClientMessagef(jcl,
|
|
"<iq id='%s' to='%s' type='result'>"
|
|
"<query xmlns='http://jabber.org/protocol/bytestreams' sid='%s'>"
|
|
"<streamhost-used jid='%s'/>"
|
|
"</query>"
|
|
"</iq>", ft->iqid, ft->with, ft->sid, ft->streamhosts[ft->nexthost].jid);
|
|
}
|
|
}
|
|
else
|
|
len = -1; //err, this is someone else...
|
|
}
|
|
}
|
|
}
|
|
|
|
if (len == -1)
|
|
{
|
|
pNet_Close(ft->stream);
|
|
ft->stream = -1;
|
|
if (ft->streamstatus == STRM_ACTIVE)
|
|
{
|
|
int size;
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
ft->file = -1;
|
|
|
|
size = pFS_Open(ft->fname, &ft->file, 1);
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
if (size == ft->size)
|
|
{
|
|
Con_Printf("File Transfer Completed\n");
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("File Transfer Aborted by peer\n");
|
|
continue;
|
|
}
|
|
}
|
|
Con_Printf("File Transfer connection to %s:%i failed\n", ft->streamhosts[ft->nexthost].host, ft->streamhosts[ft->nexthost].port);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void XMPP_FT_AcceptFile(jclient_t *jcl, int fileid, qboolean accept)
|
|
{
|
|
struct ft_s *ft, **link;
|
|
char *s;
|
|
xmltree_t *repiq, *repsi, *c;
|
|
|
|
for (link = &jcl->ft; (ft=*link); link = &(*link)->next)
|
|
{
|
|
if (ft->privateid == fileid)
|
|
break;
|
|
}
|
|
if (!ft)
|
|
{
|
|
Con_Printf("File not known\n");
|
|
return;
|
|
}
|
|
|
|
if (!accept)
|
|
{
|
|
Con_Printf("Declining file \"%s\" from \"%s\" (%i bytes)\n", ft->fname, ft->with, ft->size);
|
|
|
|
if (!ft->allowed)
|
|
{
|
|
JCL_AddClientMessagef(jcl,
|
|
"<iq type='error' to='%s' id='%s'>"
|
|
"<error type='cancel'>"
|
|
"<forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
|
|
"<text>Offer Declined</text>"
|
|
"</error>"
|
|
"</iq>", ft->with, ft->iqid);
|
|
}
|
|
else
|
|
{
|
|
//FIXME: send a proper cancel
|
|
}
|
|
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
*link = ft->next;
|
|
free(ft);
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Receiving file \"%s\" from \"%s\" (%i bytes)\n", ft->fname, ft->with, ft->size);
|
|
ft->allowed = true;
|
|
|
|
//generate a response.
|
|
//FIXME: we ought to delay response until after we prompt.
|
|
repiq = XML_CreateNode(NULL, "iq", "", "");
|
|
XML_AddParameter(repiq, "type", "result");
|
|
XML_AddParameter(repiq, "to", ft->with);
|
|
XML_AddParameter(repiq, "id", ft->iqid);
|
|
repsi = XML_CreateNode(repiq, "si", "http://jabber.org/protocol/si", "");
|
|
XML_CreateNode(repsi, "file", "http://jabber.org/protocol/si/profile/file-transfer", ""); //I don't really get the point of this.
|
|
c = XML_CreateNode(repsi, "feature", "http://jabber.org/protocol/feature-neg", "");
|
|
c = XML_CreateNode(c, "x", "jabber:x:data", "");
|
|
XML_AddParameter(c, "type", "submit");
|
|
c = XML_CreateNode(c, "field", "", "");
|
|
XML_AddParameter(c, "var", "stream-method");
|
|
if (ft->method == FT_IBB)
|
|
c = XML_CreateNode(c, "value", "", "http://jabber.org/protocol/ibb");
|
|
else if (ft->method == FT_BYTESTREAM)
|
|
c = XML_CreateNode(c, "value", "", "http://jabber.org/protocol/bytestreams");
|
|
|
|
s = XML_GenerateString(repiq, false);
|
|
JCL_AddClientMessageString(jcl, s);
|
|
free(s);
|
|
XML_Destroy(repiq);
|
|
}
|
|
}
|
|
|
|
static qboolean XMPP_FT_IBBChunked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq)
|
|
{
|
|
char *from = XML_GetParameter(x, "from", jcl->domain);
|
|
struct ft_s *ft = iq->usrptr, **link, *v;
|
|
for (link = &jcl->ft; (v=*link); link = &(*link)->next)
|
|
{
|
|
if (v == ft && !strcmp(ft->with, from))
|
|
{
|
|
//its still valid
|
|
if (x)
|
|
{
|
|
char *base64;
|
|
char rawbuf[4096];
|
|
int sz;
|
|
sz = pFS_Read(ft->file, rawbuf, ft->blocksize);
|
|
Base64_Add(rawbuf, sz);
|
|
base64 = Base64_Finish();
|
|
|
|
if (sz > 0)
|
|
{
|
|
ft->sizedone += sz;
|
|
if (ft->sizedone == ft->size)
|
|
ft->eof = true;
|
|
}
|
|
|
|
if (sz && strlen(base64))
|
|
{
|
|
x = XML_CreateNode(NULL, "data", "http://jabber.org/protocol/ibb", base64);
|
|
XML_AddParameteri(x, "seq", ft->seq++);
|
|
XML_AddParameter(x, "sid", ft->sid);
|
|
JCL_SendIQNode(jcl, XMPP_FT_IBBChunked, "set", from, x, true)->usrptr = ft;
|
|
return true;
|
|
}
|
|
//else eof
|
|
}
|
|
|
|
//errored or ended
|
|
|
|
if (x)
|
|
Con_Printf("Transfer of %s to %s completed\n", ft->fname, ft->with);
|
|
else
|
|
Con_Printf("%s aborted %s\n", ft->with, ft->fname);
|
|
x = XML_CreateNode(NULL, "close", "http://jabber.org/protocol/ibb", "");
|
|
XML_AddParameter(x, "sid", ft->sid);
|
|
JCL_SendIQNode(jcl, NULL, "set", from, x, true)->usrptr = ft;
|
|
|
|
//errored
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
*link = ft->next;
|
|
free(ft);
|
|
return true;
|
|
}
|
|
}
|
|
return true; //the ack can come after the bytestream has already finished sending. don't warn about that.
|
|
}
|
|
static qboolean XMPP_FT_IBBBegun(jclient_t *jcl, xmltree_t *x, struct iq_s *iq)
|
|
{
|
|
struct ft_s *ft = iq->usrptr, **link, *v;
|
|
char *from = XML_GetParameter(x, "from", jcl->domain);
|
|
for (link = &jcl->ft; (v=*link); link = &(*link)->next)
|
|
{
|
|
if (v == ft && !strcmp(ft->with, from))
|
|
{
|
|
//its still valid
|
|
if (!x)
|
|
{
|
|
Con_Printf("%s aborted %s\n", ft->with, ft->fname);
|
|
//errored
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
*link = ft->next;
|
|
free(ft);
|
|
}
|
|
else
|
|
{
|
|
ft->begun = true;
|
|
ft->method = FT_IBB;
|
|
XMPP_FT_IBBChunked(jcl, x, iq);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
qboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq)
|
|
{
|
|
struct ft_s *ft = iq->usrptr, **link, *v;
|
|
char *from = XML_GetParameter(x, "from", jcl->domain);
|
|
for (link = &jcl->ft; (v=*link); link = &(*link)->next)
|
|
{
|
|
if (v == ft && !strcmp(ft->with, from))
|
|
{
|
|
//its still valid
|
|
if (!x)
|
|
{
|
|
Con_Printf("%s doesn't want %s\n", ft->with, ft->fname);
|
|
//errored
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
*link = ft->next;
|
|
free(ft);
|
|
}
|
|
else
|
|
{
|
|
xmltree_t *xo;
|
|
Con_Printf("%s accepted %s\n", ft->with, ft->fname);
|
|
xo = XML_CreateNode(NULL, "open", "http://jabber.org/protocol/ibb", "");
|
|
XML_AddParameter(xo, "sid", ft->sid);
|
|
XML_AddParameteri(xo, "block-size", ft->blocksize);
|
|
//XML_AddParameter(xo, "stanza", "iq");
|
|
|
|
JCL_SendIQNode(jcl, XMPP_FT_IBBBegun, "set", XML_GetParameter(x, "from", jcl->domain), xo, true)->usrptr = ft;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void XMPP_FT_SendFile(jclient_t *jcl, char *console, char *to, char *fname)
|
|
{
|
|
xmltree_t *xsi, *xfile, *c;
|
|
struct ft_s *ft;
|
|
|
|
Con_SubPrintf(console, "Offering %s to %s.\n", fname, to);
|
|
|
|
ft = malloc(sizeof(*ft));
|
|
memset(ft, 0, sizeof(*ft));
|
|
ft->stream = -1;
|
|
ft->next = jcl->ft;
|
|
jcl->ft = ft;
|
|
ft->allowed = true;
|
|
ft->transmitting = true;
|
|
ft->blocksize = 4096;
|
|
Q_strlcpy(ft->fname, fname, sizeof(ft->fname));
|
|
Q_snprintf(ft->sid, sizeof(ft->sid), "%x%s", rand(), ft->fname);
|
|
Q_strlcpy(ft->md5hash, "", sizeof(ft->md5hash));
|
|
ft->size = pFS_Open(ft->fname, &ft->file, 1);
|
|
ft->with = strdup(to);
|
|
ft->method = FT_IBB;
|
|
ft->begun = false;
|
|
|
|
//generate an offer.
|
|
xsi = XML_CreateNode(NULL, "si", "http://jabber.org/protocol/si", "");
|
|
XML_AddParameter(xsi, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
|
|
XML_AddParameter(xsi, "id", ft->sid);
|
|
//XML_AddParameter(xsi, "mime-type", "text/plain");
|
|
xfile = XML_CreateNode(xsi, "file", "http://jabber.org/protocol/si/profile/file-transfer", ""); //I don't really get the point of this.
|
|
XML_AddParameter(xfile, "name", ft->fname);
|
|
XML_AddParameteri(xfile, "size", ft->size);
|
|
c = XML_CreateNode(xsi, "feature", "http://jabber.org/protocol/feature-neg", "");
|
|
c = XML_CreateNode(c, "x", "jabber:x:data", "");
|
|
XML_AddParameter(c, "type", "form");
|
|
c = XML_CreateNode(c, "field", "", "");
|
|
XML_AddParameter(c, "var", "stream-method");
|
|
XML_AddParameter(c, "type", "listsingle");
|
|
XML_CreateNode(XML_CreateNode(c, "option", "", ""), "value", "", "http://jabber.org/protocol/ibb");
|
|
// XML_CreateNode(XML_CreateNode(c, "option", "", ""), "value", "", "http://jabber.org/protocol/bytestreams");
|
|
|
|
JCL_SendIQNode(jcl, XMPP_FT_OfferAcked, "set", to, xsi, true)->usrptr = ft;
|
|
}
|
|
|
|
qboolean XMPP_FT_ParseIQSet(jclient_t *jcl, char *iqfrom, char *iqid, xmltree_t *tree)
|
|
{
|
|
xmltree_t *ot;
|
|
|
|
//if file transfers are not enabled in this build, reject all iqs
|
|
if (!(jcl->enabledcapabilities & CAP_SIFT))
|
|
return false;
|
|
|
|
ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/bytestreams", "query", 0);
|
|
if (ot)
|
|
{
|
|
xmltree_t *c;
|
|
struct ft_s *ft;
|
|
char *sid = XML_GetParameter(ot, "sid", "");
|
|
for (ft = jcl->ft; ft; ft = ft->next)
|
|
{
|
|
if (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom))
|
|
{
|
|
if (ft->allowed && !ft->begun && ft->transmitting == false)
|
|
{
|
|
char *jid;
|
|
char *host;
|
|
int port;
|
|
char *mode = XML_GetParameter(ot, "mode", "tcp");
|
|
int i;
|
|
if (strcmp(mode, "tcp"))
|
|
break;
|
|
for (i = 0; ; i++)
|
|
{
|
|
c = XML_ChildOfTree(ot, "streamhost", i);
|
|
if (!c)
|
|
break;
|
|
|
|
jid = XML_GetParameter(c, "jid", "");
|
|
host = XML_GetParameter(c, "host", "");
|
|
port = atoi(XML_GetParameter(c, "port", "0"));
|
|
if (port <= 0 || port > 65535)
|
|
continue;
|
|
if (ft->nexthost < sizeof(ft->streamhosts) / sizeof(ft->streamhosts[ft->nexthost]))
|
|
{
|
|
Q_strlcpy(ft->streamhosts[ft->nexthost].jid, jid, sizeof(ft->streamhosts[ft->nexthost].jid));
|
|
Q_strlcpy(ft->streamhosts[ft->nexthost].host, host, sizeof(ft->streamhosts[ft->nexthost].host));
|
|
ft->streamhosts[ft->nexthost].port = port;
|
|
ft->nexthost++;
|
|
}
|
|
}
|
|
ft->streamstatus = STRM_AUTH;
|
|
//iq gets acked when we finished connecting to the proxy. *then* the data can flow
|
|
Q_strlcpy(ft->iqid, iqid, sizeof(ft->iqid));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
JCL_AddClientMessagef(jcl, "<iq id='%s' to='%s' type='error'/>", iqid, iqfrom);
|
|
return true;
|
|
}
|
|
|
|
ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/ibb", "open", 0);
|
|
if (ot)
|
|
{
|
|
struct ft_s *ft;
|
|
char *sid = XML_GetParameter(ot, "sid", "");
|
|
int blocksize = atoi(XML_GetParameter(ot, "block-size", "4096")); //technically this is required.
|
|
char *stanza = XML_GetParameter(ot, "stanza", "iq");
|
|
for (ft = jcl->ft; ft; ft = ft->next)
|
|
{
|
|
if (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom))
|
|
{
|
|
if (ft->allowed && !ft->begun && ft->transmitting == false)
|
|
{
|
|
if (blocksize > 65536 || strcmp(stanza, "iq"))
|
|
{ //blocksize: MUST NOT be greater than 65535
|
|
JCL_AddClientMessagef(jcl,
|
|
"<iq id='%s' to='%s' type='error'>"
|
|
"<error type='modify'>"
|
|
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
|
|
"</error>"
|
|
"</iq>"
|
|
, iqid, iqfrom);
|
|
}
|
|
else if (blocksize > 4096)
|
|
{ //ask for smaller chunks
|
|
JCL_AddClientMessagef(jcl,
|
|
"<iq id='%s' to='%s' type='error'>"
|
|
"<error type='modify'>"
|
|
"<resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
|
|
"</error>"
|
|
"</iq>"
|
|
, iqid, iqfrom);
|
|
}
|
|
else
|
|
{ //it looks okay
|
|
pFS_Open(ft->fname, &ft->file, 2);
|
|
ft->method = FT_IBB;
|
|
ft->blocksize = blocksize;
|
|
ft->begun = true;
|
|
//if its okay...
|
|
JCL_AddClientMessagef(jcl, "<iq id='%s' to='%s' type='result'/>", iqid, iqfrom);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/ibb", "close", 0);
|
|
if (ot)
|
|
{
|
|
struct ft_s **link, *ft;
|
|
char *sid = XML_GetParameter(ot, "sid", "");
|
|
for (link = &jcl->ft; *link; link = &(*link)->next)
|
|
{
|
|
ft = *link;
|
|
if (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom))
|
|
{
|
|
if (ft->begun && ft->method == FT_IBB)
|
|
{
|
|
int size;
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
if (ft->transmitting)
|
|
{
|
|
if (ft->eof)
|
|
Con_Printf("Sent \"%s\" to \"%s\"\n", ft->fname, ft->with);
|
|
else
|
|
Con_Printf("%s aborted transfer of \"%s\"\n", iqfrom, ft->fname);
|
|
}
|
|
else
|
|
{
|
|
size = pFS_Open(ft->fname, &ft->file, 1);
|
|
if (ft->file != -1)
|
|
pFS_Close(ft->file);
|
|
if (size == ft->size)
|
|
Con_Printf("Received file \"%s\" successfully\n", ft->fname);
|
|
else
|
|
Con_Printf("%s aborted transfer of \"%s\"\n", iqfrom, ft->fname);
|
|
}
|
|
*link = ft->next;
|
|
free(ft);
|
|
//if its okay...
|
|
JCL_AddClientMessagef(jcl, "<iq id='%s' to='%s' type='result'/>", iqid, iqfrom);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false; //some kind of error
|
|
}
|
|
ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/ibb", "data", 0);
|
|
if (ot)
|
|
{
|
|
char block[65536];
|
|
char *sid = XML_GetParameter(ot, "sid", "");
|
|
// unsigned short seq = atoi(XML_GetParameter(ot, "seq", "0"));
|
|
int blocksize;
|
|
struct ft_s *ft;
|
|
//FIXME: validate the sequence!
|
|
for (ft = jcl->ft; ft; ft = ft->next)
|
|
{
|
|
if (!strcmp(ft->sid, sid) && !ft->transmitting)
|
|
{
|
|
blocksize = Base64_Decode(block, sizeof(block), ot->body, strlen(ot->body));
|
|
if (blocksize && blocksize <= ft->blocksize)
|
|
{
|
|
pFS_Write(ft->file, block, blocksize);
|
|
JCL_AddClientMessagef(jcl, "<iq id='%s' to='%s' type='result'/>", iqid, iqfrom);
|
|
return true;
|
|
}
|
|
else
|
|
Con_Printf("XMPP: Invalid blocksize in file transfer from %s\n", iqfrom);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ot = XML_ChildOfTreeNS(tree, "http://jabber.org/protocol/si", "si", 0);
|
|
if (ot)
|
|
{
|
|
char *profile = XML_GetParameter(ot, "profile", "");
|
|
|
|
if (!strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer"))
|
|
{
|
|
// char *mimetype = XML_GetParameter(ot, "mime-type", "text/plain");
|
|
char *sid = XML_GetParameter(ot, "id", "");
|
|
xmltree_t *file = XML_ChildOfTreeNS(ot, "http://jabber.org/protocol/si/profile/file-transfer", "file", 0);
|
|
char *fname = XML_GetParameter(file, "name", "file.txt");
|
|
// char *date = XML_GetParameter(file, "date", "");
|
|
char *md5hash = XML_GetParameter(file, "hash", "");
|
|
int fsize = strtoul(XML_GetParameter(file, "size", "0"), NULL, 0);
|
|
// char *desc = XML_GetChildBody(file, "desc", "");
|
|
char authlink[512];
|
|
char denylink[512];
|
|
|
|
//file transfer offer
|
|
struct ft_s *ft = malloc(sizeof(*ft) + strlen(iqfrom)+1);
|
|
memset(ft, 0, sizeof(*ft));
|
|
ft->stream = -1;
|
|
ft->next = jcl->ft;
|
|
jcl->ft = ft;
|
|
ft->privateid = ++jcl->privateidseq;
|
|
|
|
ft->transmitting = false;
|
|
Q_strlcpy(ft->iqid, iqid, sizeof(ft->iqid));
|
|
Q_strlcpy(ft->sid, sid, sizeof(ft->sid));
|
|
Q_strlcpy(ft->fname, fname, sizeof(ft->sid));
|
|
Base64_Decode(ft->md5hash, sizeof(ft->md5hash), md5hash, strlen(md5hash));
|
|
ft->size = fsize;
|
|
ft->file = -1;
|
|
ft->with = (char*)(ft+1);
|
|
strcpy(ft->with, iqfrom);
|
|
ft->method = (fsize > 1024*128)?FT_BYTESTREAM:FT_IBB; //favour bytestreams for large files. for small files, just use ibb as it saves sorting out proxies.
|
|
ft->begun = false;
|
|
|
|
JCL_GenLink(jcl, authlink, sizeof(authlink), "fauth", iqfrom, NULL, va("%i", ft->privateid), "%s", "Accept");
|
|
JCL_GenLink(jcl, denylink, sizeof(denylink), "fdeny", iqfrom, NULL, va("%i", ft->privateid), "%s", "Deny");
|
|
|
|
Con_Printf("%s has offered to send you \"%s\" (%i bytes). %s %s\n", iqfrom, fname, fsize, authlink, denylink);
|
|
}
|
|
else
|
|
{
|
|
//profile not understood
|
|
JCL_AddClientMessagef(jcl,
|
|
"<iq type='error' to='%s' id='%s'>"
|
|
"<error code='400' type='cancel'>"
|
|
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
|
|
"<bad-profile xmlns='http://jabber.org/protocol/si'/>"
|
|
"</error>"
|
|
"</iq>", iqfrom, iqid);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|