2009-10-01 01:10:58 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Network Operations
|
|
|
|
* for USB Loader GX
|
|
|
|
*
|
|
|
|
* HTTP operations
|
|
|
|
* Written by dhewg/bushing modified by dimok
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ogcsys.h>
|
|
|
|
#include <ogc/machine/processor.h>
|
|
|
|
|
|
|
|
#include "prompts/PromptWindows.h"
|
|
|
|
#include "language/gettext.h"
|
2010-09-19 22:25:12 +02:00
|
|
|
#include "settings/CSettings.h"
|
2009-10-01 01:10:58 +02:00
|
|
|
#include "main.h"
|
|
|
|
#include "http.h"
|
2009-11-18 10:01:14 +01:00
|
|
|
#include "svnrev.h"
|
|
|
|
#include "buildtype.h"
|
2009-12-10 21:27:36 +01:00
|
|
|
#include "update.h"
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
#define PORT 4299
|
|
|
|
|
|
|
|
/*** Incomming filesize ***/
|
|
|
|
u32 infilesize = 0;
|
|
|
|
u32 uncfilesize = 0;
|
|
|
|
|
|
|
|
bool updateavailable = false;
|
|
|
|
s32 connection;
|
|
|
|
static s32 socket;
|
|
|
|
static bool updatechecked = false;
|
|
|
|
static bool networkinitialized = false;
|
|
|
|
static bool checkincomming = false;
|
|
|
|
static bool waitforanswer = false;
|
|
|
|
static char IP[16];
|
|
|
|
char incommingIP[50];
|
|
|
|
char wiiloadVersion[2];
|
|
|
|
|
|
|
|
static lwp_t networkthread = LWP_THREAD_NULL;
|
|
|
|
static bool networkHalt = true;
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Initialize_Network
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
void Initialize_Network( void )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( networkinitialized ) return;
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
s32 result;
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
result = if_config( IP, NULL, NULL, true );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( result < 0 )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
networkinitialized = false;
|
|
|
|
return;
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
networkinitialized = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Check if network was initialised
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
bool IsNetworkInit( void )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return networkinitialized;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Get network IP
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
char * GetNetworkIP( void )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return IP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Get incomming IP
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
char * GetIncommingIP( void )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return incommingIP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Get network IP
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
bool ShutdownWC24()
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
bool onlinefix = IsNetworkInit();
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( onlinefix )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
s32 kd_fd, ret;
|
2010-09-19 01:16:05 +02:00
|
|
|
STACK_ALIGN( u8, kd_buf, 0x20, 32 );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
kd_fd = IOS_Open( "/dev/net/kd/request", 0 );
|
|
|
|
if ( kd_fd >= 0 )
|
|
|
|
{
|
|
|
|
ret = IOS_Ioctl( kd_fd, 7, NULL, 0, kd_buf, 0x20 );
|
|
|
|
if ( ret >= 0 )
|
2009-10-01 01:10:58 +02:00
|
|
|
onlinefix = false; // fixed no IOS reload needed
|
2010-09-19 01:16:05 +02:00
|
|
|
IOS_Close( kd_fd );
|
2009-10-01 01:10:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return onlinefix;
|
|
|
|
}
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
s32 network_request( const char * request, char * filename )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
char buf[1024];
|
|
|
|
char *ptr = NULL;
|
|
|
|
|
|
|
|
u32 cnt, size;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Send request */
|
2010-09-19 01:16:05 +02:00
|
|
|
ret = net_send( connection, request, strlen( request ), 0 );
|
|
|
|
if ( ret < 0 )
|
2009-10-01 01:10:58 +02:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Clear buffer */
|
2010-09-19 01:16:05 +02:00
|
|
|
memset( buf, 0, sizeof( buf ) );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
/* Read HTTP header */
|
2010-09-19 01:16:05 +02:00
|
|
|
for ( cnt = 0; !strstr( buf, "\r\n\r\n" ); cnt++ )
|
|
|
|
if ( net_recv( connection, buf + cnt, 1, 0 ) <= 0 )
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* HTTP request OK? */
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( !strstr( buf, "HTTP/1.1 200 OK" ) )
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
2009-11-02 23:15:28 +01:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( filename )
|
2010-02-09 11:59:55 +01:00
|
|
|
{
|
2009-11-02 23:15:28 +01:00
|
|
|
/* Get filename */
|
2010-09-19 01:16:05 +02:00
|
|
|
ptr = strstr( buf, "filename=\"" );
|
2009-11-02 23:15:28 +01:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( ptr )
|
2010-02-09 11:59:55 +01:00
|
|
|
{
|
2010-09-19 01:16:05 +02:00
|
|
|
ptr += sizeof( "filename=\"" ) - 1;
|
2009-11-02 23:15:28 +01:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
for ( cnt = 0; ptr[cnt] != '\r' && ptr[cnt] != '\n' && ptr[cnt] != '"'; cnt++ )
|
2010-02-09 11:59:55 +01:00
|
|
|
{
|
2009-11-02 23:15:28 +01:00
|
|
|
filename[cnt] = ptr[cnt];
|
|
|
|
filename[cnt+1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-01 01:10:58 +02:00
|
|
|
/* Retrieve content size */
|
2010-09-19 01:16:05 +02:00
|
|
|
ptr = strstr( buf, "Content-Length:" );
|
|
|
|
if ( !ptr )
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
sscanf( ptr, "Content-Length: %u", &size );
|
2009-10-01 01:10:58 +02:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
s32 network_read( u8 *buf, u32 len )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
u32 read = 0;
|
|
|
|
s32 ret = -1;
|
|
|
|
|
|
|
|
/* Data to be read */
|
2010-09-19 01:16:05 +02:00
|
|
|
while ( read < len )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
/* Read network data */
|
2010-09-19 01:16:05 +02:00
|
|
|
ret = net_read( connection, buf + read, len - read );
|
|
|
|
if ( ret < 0 )
|
2009-10-01 01:10:58 +02:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Read finished */
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( !ret )
|
2009-10-01 01:10:58 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Increment read variable */
|
|
|
|
read += ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return read;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Download request
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
s32 download_request( const char * url, char * filename )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
//Check if the url starts with "http://", if not it is not considered a valid url
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( strncmp( url, "http://", strlen( "http://" ) ) != 0 )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Locate the path part of the url by searching for '/' past "http://"
|
2010-09-19 01:16:05 +02:00
|
|
|
char *path = strchr( url + strlen( "http://" ), '/' );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
//At the very least the url has to end with '/', ending with just a domain is invalid
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( path == NULL )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Extract the domain part out of the url
|
2010-09-19 01:16:05 +02:00
|
|
|
int domainlength = path - url - strlen( "http://" );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( domainlength == 0 )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char domain[domainlength + 1];
|
2010-09-19 01:16:05 +02:00
|
|
|
strlcpy( domain, url + strlen( "http://" ), domainlength + 1 );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
connection = GetConnection( domain );
|
|
|
|
if ( connection < 0 )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Form a nice request header to send to the webserver
|
2010-09-19 01:16:05 +02:00
|
|
|
char header[strlen( path )+strlen( domain )+strlen( url )+100];
|
|
|
|
sprintf( header, "GET %s HTTP/1.1\r\nHost: %s\r\nReferer: %s\r\nConnection: close\r\n\r\n", path, domain, url );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
s32 filesize = network_request( header, filename );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
return filesize;
|
|
|
|
}
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
void CloseConnection()
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
net_close( connection );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( waitforanswer )
|
|
|
|
{
|
|
|
|
net_close( socket );
|
2009-10-01 01:10:58 +02:00
|
|
|
waitforanswer = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* NetworkWait
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
int NetworkWait()
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( !checkincomming )
|
2009-10-01 01:10:58 +02:00
|
|
|
return -3;
|
|
|
|
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
struct sockaddr_in client_address;
|
2010-09-19 01:16:05 +02:00
|
|
|
socklen_t addrlen = sizeof( client_address );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
//Open socket
|
2010-09-19 01:16:05 +02:00
|
|
|
socket = net_socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( socket == INVALID_SOCKET )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
sin.sin_family = AF_INET;
|
2010-09-19 01:16:05 +02:00
|
|
|
sin.sin_port = htons( PORT );
|
|
|
|
sin.sin_addr.s_addr = htonl( INADDR_ANY );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( net_bind( socket, ( struct sockaddr* )&sin, sizeof( sin ) ) < 0 )
|
|
|
|
{
|
|
|
|
net_close( socket );
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( net_listen( socket, 3 ) < 0 )
|
|
|
|
{
|
|
|
|
net_close( socket );
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
connection = net_accept( socket, ( struct sockaddr* ) & client_address, &addrlen );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
sprintf( incommingIP, "%s", inet_ntoa( client_address.sin_addr ) );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( connection < 0 )
|
|
|
|
{
|
|
|
|
net_close( connection );
|
|
|
|
net_close( socket );
|
2009-10-01 01:10:58 +02:00
|
|
|
return -4;
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
unsigned char haxx[9];
|
|
|
|
//skip haxx
|
2010-09-19 01:16:05 +02:00
|
|
|
net_read( connection, &haxx, 8 );
|
|
|
|
wiiloadVersion[0] = haxx[4];
|
|
|
|
wiiloadVersion[1] = haxx[5];
|
2009-10-21 21:32:46 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
net_read( connection, &infilesize, 4 );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( haxx[4] > 0 || haxx[5] > 4 )
|
|
|
|
{
|
|
|
|
net_read( connection, &uncfilesize, 4 ); // Compressed protocol, read another 4 bytes
|
|
|
|
}
|
2009-10-01 01:10:58 +02:00
|
|
|
waitforanswer = true;
|
|
|
|
checkincomming = false;
|
|
|
|
networkHalt = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Update check
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
int CheckUpdate()
|
|
|
|
{
|
|
|
|
if ( !networkinitialized )
|
2009-10-01 01:10:58 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
int revnumber = 0;
|
2010-09-19 01:16:05 +02:00
|
|
|
int currentrev = atoi( GetRev() );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( Settings.beta_upgrades )
|
|
|
|
{
|
|
|
|
revnumber = CheckForBetaUpdate();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-18 10:01:14 +01:00
|
|
|
#ifdef FULLCHANNEL
|
2010-09-19 01:16:05 +02:00
|
|
|
struct block file = downloadfile( "http://www.techjawa.com/usbloadergx/wadrev.txt" );
|
2009-11-18 10:01:14 +01:00
|
|
|
#else
|
2010-09-19 01:16:05 +02:00
|
|
|
struct block file = downloadfile( "http://www.techjawa.com/usbloadergx/rev.txt" );
|
2009-10-01 01:10:58 +02:00
|
|
|
#endif
|
2010-09-19 01:16:05 +02:00
|
|
|
char revtxt[10];
|
|
|
|
|
|
|
|
u8 i;
|
|
|
|
if ( file.data != NULL )
|
|
|
|
{
|
|
|
|
for ( i = 0; i < 9 || i < file.size; i++ )
|
|
|
|
revtxt[i] = file.data[i];
|
|
|
|
revtxt[i] = 0;
|
|
|
|
revnumber = atoi( revtxt );
|
|
|
|
free( file.data );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( revnumber > currentrev )
|
2009-10-01 01:10:58 +02:00
|
|
|
return revnumber;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* HaltNetwork
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
void HaltNetworkThread()
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
networkHalt = true;
|
|
|
|
checkincomming = false;
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( waitforanswer )
|
2009-10-01 01:10:58 +02:00
|
|
|
CloseConnection();
|
|
|
|
|
|
|
|
// wait for thread to finish
|
2010-09-19 01:16:05 +02:00
|
|
|
while ( !LWP_ThreadIsSuspended( networkthread ) )
|
|
|
|
usleep( 100 );
|
2009-10-01 01:10:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* ResumeNetworkThread
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
void ResumeNetworkThread()
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
networkHalt = false;
|
2010-09-19 01:16:05 +02:00
|
|
|
LWP_ResumeThread( networkthread );
|
2009-10-01 01:10:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Resume NetworkWait
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
void ResumeNetworkWait()
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
networkHalt = true;
|
|
|
|
checkincomming = true;
|
|
|
|
waitforanswer = true;
|
|
|
|
infilesize = 0;
|
|
|
|
connection = -1;
|
2010-09-19 01:16:05 +02:00
|
|
|
LWP_ResumeThread( networkthread );
|
2009-10-01 01:10:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************
|
|
|
|
* Networkthread for background network initialize and update check with idle prio
|
|
|
|
*********************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
static void * networkinitcallback( void *arg )
|
|
|
|
{
|
|
|
|
while ( 1 )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( !checkincomming && networkHalt )
|
|
|
|
LWP_SuspendThread( networkthread );
|
2009-10-01 01:10:58 +02:00
|
|
|
|
|
|
|
Initialize_Network();
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( networkinitialized == true && updatechecked == false )
|
|
|
|
{
|
2009-10-01 01:10:58 +02:00
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( CheckUpdate() > 0 )
|
2009-10-01 01:10:58 +02:00
|
|
|
updateavailable = true;
|
|
|
|
|
|
|
|
//suspend thread
|
|
|
|
updatechecked = true;
|
|
|
|
networkHalt = true;
|
|
|
|
checkincomming = false;
|
|
|
|
}
|
|
|
|
|
2010-09-19 01:16:05 +02:00
|
|
|
if ( checkincomming )
|
2009-10-01 01:10:58 +02:00
|
|
|
NetworkWait();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* InitNetworkThread with priority 0 (idle)
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
void InitNetworkThread()
|
|
|
|
{
|
|
|
|
LWP_CreateThread ( &networkthread, networkinitcallback, NULL, NULL, 16384, 0 );
|
2009-10-01 01:10:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* ShutdownThread
|
|
|
|
***************************************************************************/
|
2010-09-19 01:16:05 +02:00
|
|
|
void ShutdownNetworkThread()
|
|
|
|
{
|
|
|
|
LWP_JoinThread ( networkthread, NULL );
|
2009-10-01 01:10:58 +02:00
|
|
|
networkthread = LWP_THREAD_NULL;
|
|
|
|
}
|