533 lines
14 KiB
C
533 lines
14 KiB
C
#include "../qcommon/q_shared.h"
|
|
#include "../qcommon/qcommon.h"
|
|
|
|
#include "../../libs/curl-7.15.3/include/curl/curl.h"
|
|
|
|
// http access using cUrl. p.s. cUrl sucks donkey balls
|
|
|
|
#define MAX_COOKIE_SIZE 256
|
|
|
|
typedef struct {
|
|
CURL * handle;
|
|
void * notifyData;
|
|
HTTP_response response;
|
|
char postfields[ 4096 ];
|
|
int code;
|
|
|
|
|
|
struct curl_httppost * firstitem;
|
|
struct curl_httppost * lastitem;
|
|
struct curl_slist * headerlist;
|
|
|
|
} httpTaskInfo_t;
|
|
|
|
typedef struct {
|
|
|
|
CURLM * multi_handle;
|
|
int still_running;
|
|
struct curl_slist * headers;
|
|
|
|
} httpInfo_t;
|
|
|
|
|
|
static httpInfo_t http;
|
|
static httpTaskInfo_t tasks[ 8 ];
|
|
|
|
cvar_t *http_bugauth;
|
|
cvar_t * http_usernameandpassword;
|
|
|
|
/*
|
|
==================
|
|
Net_HTTP_write_callback
|
|
|
|
this is called when there is data to be receieved from the websever
|
|
==================
|
|
*/
|
|
size_t QDECL Net_HTTP_write_callback( const char * buffer, size_t size, size_t nitems, void * outstream ) {
|
|
httpTaskInfo_t * task = (httpTaskInfo_t*)outstream;
|
|
|
|
if ( task->response && task->code < 300 ) {
|
|
return task->response( HTTP_WRITE, buffer, size * nitems, task->notifyData );
|
|
}
|
|
|
|
return size*nitems;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Net_HTTP_read_callback
|
|
|
|
this is called when there is data to be receieved from the websever
|
|
==================
|
|
*/
|
|
size_t QDECL Net_HTTP_read_callback( const char * buffer, size_t size, size_t nitems, void * outstream ) {
|
|
|
|
httpTaskInfo_t * task = (httpTaskInfo_t*)outstream;
|
|
|
|
if ( task->response ) {
|
|
return task->response( HTTP_READ, buffer, size * nitems, task->notifyData );
|
|
}
|
|
|
|
return size*nitems;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Net_HTTP_Init
|
|
==================
|
|
*/
|
|
int Net_HTTP_Init()
|
|
{
|
|
http.multi_handle = curl_multi_init();
|
|
|
|
if ( http.multi_handle == NULL ) {
|
|
return 0;
|
|
}
|
|
|
|
http_usernameandpassword = Cvar_Get( "usernameandpassword", "username:password", CVAR_ARCHIVE );
|
|
http_bugauth = Cvar_Get( "http_bugauth", "username:password", CVAR_ARCHIVE );
|
|
|
|
http.headers = curl_slist_append( http.headers, "Accept: application/spacetrader" );
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
==================
|
|
*/
|
|
void Net_HTTP_Cleanup( CURL * handle )
|
|
{
|
|
curl_multi_remove_handle ( http.multi_handle, handle );
|
|
curl_easy_cleanup ( handle );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
==================
|
|
*/
|
|
void Net_HTTP_Kill()
|
|
{
|
|
int i;
|
|
|
|
curl_slist_free_all ( http.headers );
|
|
http.headers = NULL;
|
|
|
|
for( i = 0; i < lengthof( tasks ); i++ ){
|
|
if( tasks[i].handle ){
|
|
Net_HTTP_Cleanup( tasks[i].handle );
|
|
tasks[i].handle = NULL;
|
|
}
|
|
}
|
|
curl_multi_cleanup ( http.multi_handle );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
==================
|
|
*/
|
|
void Net_HTTP_EscapeField( char *out_url, const char *in_url, int len )
|
|
{
|
|
char *url;
|
|
url = curl_escape(in_url, 0);
|
|
Q_strncpyz( out_url, url, len);
|
|
curl_free(url);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
==================
|
|
*/
|
|
size_t QDECL Net_HTTP_ParseHeader( const char * ptr, size_t size, size_t nmemb, void *stream){
|
|
|
|
httpTaskInfo_t * task = (httpTaskInfo_t*)stream;
|
|
|
|
if ( task->response ) {
|
|
|
|
int httpversion_major, httpversion, httpcode;
|
|
|
|
if ( sscanf( ptr, " HTTP/%d.%d %3d", &httpversion_major, &httpversion, &httpcode ) == 3 ) {
|
|
|
|
task->code = httpcode;
|
|
|
|
if ( httpcode >= 400 ) {
|
|
|
|
Com_Error( ERR_DROP, "HTTP Error: (%d)\n", httpcode );
|
|
|
|
task->response( HTTP_FAILED, ptr, httpcode, task->notifyData );
|
|
task->response = 0; // don't send anymore messages
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( task->code < 300 ) {
|
|
char * title = COM_Parse( &ptr );
|
|
if ( !Q_stricmp( title, "Content-Length:" ) ) {
|
|
char * value = COM_Parse( &ptr );
|
|
task->response( HTTP_LENGTH, value, strlen(value), task->notifyData );
|
|
} else if ( !Q_stricmp( title, "Set-Cookie:" ) ) {
|
|
#ifdef USE_WEBAUTH
|
|
char tmp[ MAX_INFO_STRING ];
|
|
|
|
for ( tmp[ 0 ] = '\0'; ; ) {
|
|
|
|
char * value = COM_ParseExt( &ptr, qfalse );
|
|
|
|
if ( value && value[0] ) {
|
|
Q_strcat( tmp, sizeof(tmp), value );
|
|
Q_strcat( tmp, sizeof(tmp), " " );
|
|
} else
|
|
break;
|
|
}
|
|
|
|
Cvar_Set( "com_sessionid", tmp );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return size * nmemb;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
==================
|
|
*/
|
|
int Net_HTTP_Pump()
|
|
{
|
|
struct timeval timeout;
|
|
int rc; /* select() return code */
|
|
fd_set fdread;
|
|
fd_set fdwrite;
|
|
fd_set fdexcep;
|
|
int maxfd;
|
|
|
|
if ( !http.still_running )
|
|
return 0;
|
|
|
|
FD_ZERO(&fdread);
|
|
FD_ZERO(&fdwrite);
|
|
FD_ZERO(&fdexcep);
|
|
|
|
/* we are polling, so no timeout */
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 0;
|
|
|
|
/* get file descriptors from the transfers */
|
|
curl_multi_fdset( http.multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd );
|
|
|
|
rc = select( maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout );
|
|
switch(rc)
|
|
{
|
|
case -1:
|
|
/* select error */
|
|
break;
|
|
case 0:
|
|
/* timeout, do something else */
|
|
break;
|
|
default:
|
|
{
|
|
/* one or more of curl's file descriptors say there's data to read or write */
|
|
while( CURLM_CALL_MULTI_PERFORM == curl_multi_perform( http.multi_handle, &http.still_running) );
|
|
|
|
} break;
|
|
}
|
|
|
|
if ( !http.still_running ) {
|
|
int i;
|
|
for ( i=0; i<lengthof( tasks ); i++ ) {
|
|
|
|
if ( tasks[ i ].handle ) {
|
|
if ( tasks[ i ].response && tasks[ i ].code < 300 ) {
|
|
tasks[ i ].response( HTTP_DONE, 0, 0, tasks[i].notifyData );
|
|
}
|
|
if ( tasks[i].firstitem )
|
|
curl_formfree( tasks[i].firstitem );
|
|
|
|
if ( tasks[i].headerlist )
|
|
curl_slist_free_all( tasks[i].headerlist );
|
|
|
|
curl_multi_remove_handle( http.multi_handle, tasks[i].handle );
|
|
curl_easy_cleanup ( tasks[i].handle );
|
|
|
|
Com_Memset( tasks+i, 0, sizeof(httpTaskInfo_t) );
|
|
}
|
|
}
|
|
}
|
|
|
|
return http.still_running;
|
|
}
|
|
|
|
|
|
static httpTaskInfo_t * new_task() {
|
|
int i;
|
|
for ( i=0; i<lengthof( tasks ); i++ ) {
|
|
if ( tasks[i].handle == NULL ) {
|
|
Com_Memset( tasks+i, 0, sizeof(httpTaskInfo_t) );
|
|
return tasks + i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
HTTP_GetUrl
|
|
==================
|
|
*/
|
|
void HTTP_GetUrl( const char * url, HTTP_response response, void * notifyData, int resume_from ) {
|
|
|
|
CURL * handle = curl_easy_init();
|
|
httpTaskInfo_t * task = new_task();
|
|
if ( !task )
|
|
return;
|
|
|
|
task->handle = handle;
|
|
task->notifyData = notifyData;
|
|
task->response = response;
|
|
|
|
curl_easy_setopt( handle, CURLOPT_URL, url );
|
|
curl_easy_setopt( handle, CURLOPT_HEADERFUNCTION, Net_HTTP_ParseHeader );
|
|
curl_easy_setopt( handle, CURLOPT_HEADERDATA, task );
|
|
curl_easy_setopt( handle, CURLOPT_CONNECTTIMEOUT, 5 );
|
|
curl_easy_setopt( handle, CURLOPT_NOSIGNAL, 1 );
|
|
curl_easy_setopt( handle, CURLOPT_HTTPHEADER, http.headers );
|
|
curl_easy_setopt( handle, CURLOPT_RESUME_FROM, resume_from );
|
|
curl_easy_setopt( handle, CURLOPT_FOLLOWLOCATION, 1 );
|
|
|
|
//turn on http auth since we have the website password protected
|
|
curl_easy_setopt( handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
|
|
curl_easy_setopt( handle, CURLOPT_USERPWD, http_usernameandpassword->string );
|
|
|
|
curl_easy_setopt( handle, CURLOPT_WRITEFUNCTION, Net_HTTP_write_callback );
|
|
curl_easy_setopt( handle, CURLOPT_WRITEDATA, task );
|
|
|
|
#ifdef USE_WEBAUTH
|
|
if ( com_sessionid->string[0] )
|
|
curl_easy_setopt( handle, CURLOPT_COOKIE, com_sessionid->string );
|
|
#endif
|
|
|
|
curl_multi_add_handle( http.multi_handle, handle );
|
|
|
|
//make sure CURL is ready for this handle
|
|
//spin here so when we pump later on it's ready
|
|
while( CURLM_CALL_MULTI_PERFORM == curl_multi_perform( http.multi_handle, &http.still_running) );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
HTTP_PostUrl
|
|
==================
|
|
*/
|
|
void HTTP_PostUrl( const char * url, HTTP_response response, void * notifyData, const char * fmt, ... ) {
|
|
|
|
CURL * handle = curl_easy_init();
|
|
httpTaskInfo_t * task = new_task();
|
|
if ( !task )
|
|
return;
|
|
|
|
if ( fmt ) {
|
|
va_list arglist;
|
|
va_start(arglist, fmt);
|
|
vsnprintf( task->postfields, sizeof(task->postfields), fmt, arglist );
|
|
va_end(arglist);
|
|
} else
|
|
task->postfields[ 0 ] = '\0';
|
|
|
|
task->handle = handle;
|
|
task->notifyData = notifyData;
|
|
task->response = response;
|
|
|
|
curl_easy_setopt( handle, CURLOPT_URL, url );
|
|
curl_easy_setopt( handle, CURLOPT_HEADERFUNCTION, Net_HTTP_ParseHeader );
|
|
curl_easy_setopt( handle, CURLOPT_HEADERDATA, task );
|
|
curl_easy_setopt( handle, CURLOPT_CONNECTTIMEOUT, 5 );
|
|
curl_easy_setopt( handle, CURLOPT_NOSIGNAL, 1 );
|
|
curl_easy_setopt( handle, CURLOPT_HTTPHEADER, http.headers );
|
|
curl_easy_setopt( handle, CURLOPT_FOLLOWLOCATION, 1 );
|
|
|
|
if ( fmt ) {
|
|
curl_easy_setopt( handle, CURLOPT_POSTFIELDS, task->postfields );
|
|
}
|
|
|
|
#if 0
|
|
//turn on http auth since we have the website password protected
|
|
curl_easy_setopt( handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
curl_easy_setopt( handle, CURLOPT_USERPWD, http_usernameandpassword->string );
|
|
#endif
|
|
|
|
curl_easy_setopt( handle, CURLOPT_WRITEFUNCTION, Net_HTTP_write_callback );
|
|
curl_easy_setopt( handle, CURLOPT_WRITEDATA, task );
|
|
|
|
#ifdef USE_WEBAUTH
|
|
if ( com_sessionid && com_sessionid->string[0] )
|
|
curl_easy_setopt( handle, CURLOPT_COOKIE, com_sessionid->string );
|
|
#endif
|
|
|
|
curl_multi_add_handle( http.multi_handle, handle );
|
|
|
|
//make sure CURL is ready for this handle
|
|
//spin here so when we pump later on it's ready
|
|
while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform( http.multi_handle, &http.still_running));
|
|
}
|
|
#ifndef DEDICATED
|
|
extern const char* Q_EXTERNAL_CALL Con_GetText ( int console );
|
|
|
|
void HTTP_PostBug( const char *fileName ) {
|
|
|
|
cvar_t * map = Cvar_Get( "mapname", "", 0 );
|
|
cvar_t * bugreport = Cvar_Get( "r_bugreport", "", 0 );
|
|
cvar_t * challenge = Cvar_Get( "g_missiontitle", "", 0 );
|
|
cvar_t * username = Cvar_Get( "ui_username", "", 0 );
|
|
|
|
char comment[ 512 ];
|
|
const char *condump;
|
|
|
|
CURL * handle = curl_easy_init();
|
|
httpTaskInfo_t * task = new_task();
|
|
if ( !task )
|
|
return;
|
|
|
|
Q_strncpyz( comment, "[ ", sizeof(comment) );
|
|
if( username->string[0] )
|
|
Q_strcat( comment, sizeof( comment ), va( "{%s} ", username->string ) );
|
|
|
|
if ( challenge->string[ 0 ] )
|
|
Q_strcat( comment, sizeof(comment), va("< %s > : ", challenge->string ) );
|
|
|
|
Q_strcat( comment, sizeof(comment), (map->string[0])?map->string:"frontend" );
|
|
Q_strcat( comment, sizeof(comment), va( " ] %s", bugreport->string ) );
|
|
|
|
condump = Con_GetText(0);
|
|
|
|
|
|
task->handle = handle;
|
|
task->notifyData = NULL;
|
|
task->response = NULL;
|
|
task->firstitem = NULL;
|
|
task->lastitem = NULL;
|
|
task->headerlist = NULL;
|
|
|
|
curl_formadd( &task->firstitem, &task->lastitem,
|
|
CURLFORM_COPYNAME, "project_id",
|
|
CURLFORM_COPYCONTENTS, "7",
|
|
CURLFORM_END );
|
|
|
|
curl_formadd( &task->firstitem, &task->lastitem,
|
|
CURLFORM_COPYNAME, "summary",
|
|
CURLFORM_COPYCONTENTS, comment,
|
|
CURLFORM_END );
|
|
|
|
curl_formadd( &task->firstitem, &task->lastitem,
|
|
CURLFORM_COPYNAME, "description",
|
|
CURLFORM_COPYCONTENTS, bugreport->string,
|
|
CURLFORM_END );
|
|
|
|
curl_formadd( &task->firstitem, &task->lastitem,
|
|
CURLFORM_COPYNAME, "steps_to_reproduce",
|
|
CURLFORM_COPYCONTENTS, condump,
|
|
CURLFORM_END );
|
|
|
|
if ( fileName ) {
|
|
|
|
char ospath[MAX_OSPATH];
|
|
FS_BuildOSHomePath( ospath, sizeof(ospath), fileName );
|
|
curl_formadd( &task->firstitem, &task->lastitem,
|
|
CURLFORM_COPYNAME, "file",
|
|
CURLFORM_FILE, ospath,
|
|
CURLFORM_CONTENTTYPE, "image/png",
|
|
CURLFORM_END );
|
|
}
|
|
|
|
curl_easy_setopt( handle, CURLOPT_URL, "http://example.com/mantis/bug_report.php" );
|
|
curl_easy_setopt( handle, CURLOPT_HTTPHEADER, task->headerlist );
|
|
curl_easy_setopt( handle, CURLOPT_HTTPPOST, task->firstitem );
|
|
|
|
//turn on http auth since we have the website password protected
|
|
curl_easy_setopt( handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
curl_easy_setopt( handle, CURLOPT_USERPWD, "username:password" );
|
|
curl_easy_setopt( handle, CURLOPT_FOLLOWLOCATION, 1 );
|
|
|
|
|
|
curl_multi_add_handle( http.multi_handle, handle );
|
|
|
|
//make sure CURL is ready for this handle
|
|
//spin here so when we pump later on it's ready
|
|
while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform( http.multi_handle, &http.still_running));
|
|
}
|
|
|
|
void HTTP_PostErrorNotice( const char *type, const char *msg )
|
|
{
|
|
cvar_t * map = Cvar_Get( "mapname", "", 0 );
|
|
cvar_t * challenge = Cvar_Get( "g_missiontitle", "", 0 );
|
|
cvar_t * username = Cvar_Get( "ui_username", "", 0 );
|
|
|
|
char comment[ 512 ];
|
|
const char *condump;
|
|
|
|
CURL * handle;
|
|
struct curl_httppost *first, *last;
|
|
|
|
if( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK )
|
|
return;
|
|
|
|
handle = curl_easy_init();
|
|
if( !handle )
|
|
return;
|
|
|
|
Q_strncpyz( comment, type, sizeof( comment ) );
|
|
Q_strncpyz( comment, " [ ", sizeof(comment) );
|
|
if( username->string[0] )
|
|
Q_strcat( comment, sizeof( comment ), va( "{%s} ", username->string ) );
|
|
|
|
if ( challenge->string[ 0 ] )
|
|
Q_strcat( comment, sizeof(comment), va("< %s > : ", challenge->string ) );
|
|
|
|
Q_strcat( comment, sizeof(comment), (map->string[0])?map->string:"frontend" );
|
|
Q_strcat( comment, sizeof(comment), va( " ] %s", msg ) );
|
|
|
|
condump = Con_GetText(0);
|
|
|
|
first = NULL;
|
|
last = NULL;
|
|
|
|
curl_formadd( &first, &last,
|
|
CURLFORM_COPYNAME, "project_id",
|
|
CURLFORM_COPYCONTENTS, "7",
|
|
CURLFORM_END );
|
|
|
|
curl_formadd( &first, &last,
|
|
CURLFORM_COPYNAME, "summary",
|
|
CURLFORM_COPYCONTENTS, comment,
|
|
CURLFORM_END );
|
|
|
|
curl_formadd( &first, &last,
|
|
CURLFORM_COPYNAME, "description",
|
|
CURLFORM_COPYCONTENTS, msg,
|
|
CURLFORM_END );
|
|
|
|
curl_formadd( &first, &last,
|
|
CURLFORM_COPYNAME, "steps_to_reproduce",
|
|
CURLFORM_COPYCONTENTS, condump,
|
|
CURLFORM_END );
|
|
|
|
curl_easy_setopt( handle, CURLOPT_URL, "http://example.com/mantis/bug_report.php" );
|
|
//curl_easy_setopt( handle, CURLOPT_HTTPHEADER, NULL );
|
|
curl_easy_setopt( handle, CURLOPT_HTTPPOST, first );
|
|
|
|
//turn on http auth since we have the website password protected
|
|
curl_easy_setopt( handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
curl_easy_setopt( handle, CURLOPT_USERPWD, http_bugauth->string );
|
|
curl_easy_setopt( handle, CURLOPT_FOLLOWLOCATION, 1 );
|
|
|
|
|
|
curl_easy_perform( handle );
|
|
curl_easy_cleanup( handle );
|
|
curl_formfree( first );
|
|
}
|
|
|
|
#endif
|