#include "ctcs.h" // def.h
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "btcontent.h"
#include "setnonblock.h"
#include "connect_nonb.h"
#include "tracker.h"
#include "peerlist.h"
#include "peer.h"
#include "btconfig.h"
#include "bttime.h"
#include "console.h"
#ifndef HAVE_SNPRINTF
#include "compat.h"
#endif
#define CTCS_PROTOCOL 3
#define compset(a,member) ( (a.member==member)? 0 : ((a.member = member)||1) )
Ctcs CTCS;
Ctcs::Ctcs()
{
memset(m_host,0,MAXHOSTNAMELEN);
m_sock = INVALID_SOCKET;
m_port = 2780;
m_status = T_FREE;
m_interval = 5;
m_protocol = CTCS_PROTOCOL;
m_last_timestamp = m_sent_ctstatus_time = m_statustime = (time_t) 0;
m_sent_ctstatus = 0;
m_sent_ctbw = 0;
}
Ctcs::~Ctcs()
{
if( m_sock != INVALID_SOCKET) CLOSE_SOCKET(m_sock);
}
void Ctcs::Reset(time_t new_interval)
{
int warn = 0;
if(new_interval) m_interval = new_interval;
if( INVALID_SOCKET != m_sock ){
if( T_READY==m_status ) warn = 1;
CLOSE_SOCKET(m_sock);
m_sock = INVALID_SOCKET;
}
in_buffer.Reset();
out_buffer.Reset();
m_last_timestamp = now;
m_sent_ctstatus = 0;
m_sent_ctbw = 0;
m_status = T_FREE;
if( warn ) CONSOLE.Warning(2, "Connection to CTCS closed");
}
// borrowed from tracker.cpp (with changes)
int Ctcs:: _s2sin(char *h,int p,struct sockaddr_in *psin)
{
psin->sin_family = AF_INET;
psin->sin_port = htons(p);
psin->sin_addr.s_addr = inet_addr(h);
if(psin->sin_addr.s_addr == INADDR_NONE){
struct hostent *ph = gethostbyname(h);
if( !ph || ph->h_addrtype != AF_INET){
memset(psin,0,sizeof(struct sockaddr_in));
return -1;
}
memcpy(&psin->sin_addr,ph->h_addr_list[0],sizeof(struct in_addr));
}
return ( psin->sin_addr.s_addr == INADDR_NONE ) ? -1 : 0;
}
int Ctcs::CheckMessage()
{
ssize_t r;
size_t q;
r = in_buffer.FeedIn(m_sock);
// This differs from tracker.cpp since we maintain a persistent connection.
if( r == 0 ) return 0; // no data (should return an error)
q = in_buffer.Count();
if( !q ){
int error = 0;
socklen_t n = sizeof(error);
if( getsockopt(m_sock, SOL_SOCKET,SO_ERROR,&error,&n) < 0 )
error = errno;
if( error != 0 ) CONSOLE.Warning(2,
"warn, received nothing from CTCS: %s", strerror(error));
Reset(5);
return -1;
}
char *s, *msgbuf;
while(in_buffer.Count() &&
(s=strpbrk(msgbuf=in_buffer.BasePointer(), "\r\n"))){
*s = '\0';
if(arg_verbose && s!=msgbuf) CONSOLE.Debug("CTCS: %s", msgbuf);
if( !strncmp("SETDLIMIT",msgbuf,9) ){
int arg = (int)strtod(msgbuf+10, NULL);
if( !BTCONTENT.IsFull() || arg < cfg_max_bandwidth_down ){
cfg_max_bandwidth_down = arg;
if(arg_verbose) CONSOLE.Debug("DLimit=%d", cfg_max_bandwidth_down);
}
}else if( !strncmp("SETULIMIT",msgbuf,9) ){
cfg_max_bandwidth_up = (int)(strtod(msgbuf+10, NULL));
if(arg_verbose) CONSOLE.Debug("ULimit=%d", cfg_max_bandwidth_up);
}else if( !strncmp("SENDPEERS",msgbuf,9) ){
Send_Peers();
}else if( !strncmp("SENDSTATUS",msgbuf,10) ){
Send_Status();
}else if( !strncmp("SENDCONF",msgbuf,8) ){
Send_Config();
}else if( !strncmp("CTCONFIG",msgbuf,8) ){
Set_Config(msgbuf);
}else if( !strncmp("SENDDETAIL",msgbuf,10) ){
Send_Detail();
}else if( !strncmp("CTQUIT",msgbuf,6) ){
CONSOLE.Print("CTCS sent Quit command");
Tracker.ClearRestart();
Tracker.SetStoped();
}else if( !strncmp("CTRESTART",msgbuf,9) ){
Tracker.RestartTracker();
}else if( !strncmp("CTUPDATE",msgbuf,8) ){
Tracker.Reset(15);
}else if( !strncmp("PROTOCOL",msgbuf,8) ){
int proto = atoi(msgbuf+9);
if( proto <= CTCS_PROTOCOL ) m_protocol = proto;
else m_protocol = CTCS_PROTOCOL;
}else if( s!=msgbuf ){
if(arg_verbose) CONSOLE.Debug("unknown CTCS message: %s", msgbuf);
}
in_buffer.PickUp(s-msgbuf + 1);
}
m_last_timestamp = now;
return 0;
}
int Ctcs::SendMessage(const char *message)
{
int len, r=0;
char buf[CTCS_BUFSIZE];
if( m_status == T_READY ){
len = strlen(message);
strncpy(buf, message, len);
if( len+1 < CTCS_BUFSIZE ){
buf[len] = '\n';
buf[len+1] = '\0';
}else{
buf[CTCS_BUFSIZE-2] = '\n';
buf[CTCS_BUFSIZE-1] = '\0';
}
r = out_buffer.PutFlush(m_sock, buf, len+1);
if( r<0 ) Reset(5);
else m_last_timestamp = now;
}
return (r < 0) ? r : 0;
}
int Ctcs::Send_Auth()
{
char message[CTCS_BUFSIZE];
if(!*m_pass) return 0;
snprintf(message, CTCS_BUFSIZE, "AUTH %s", m_pass);
return SendMessage(message);
}
int Ctcs::Send_Protocol()
{
char message[CTCS_BUFSIZE];
snprintf(message, CTCS_BUFSIZE, "PROTOCOL %04d", CTCS_PROTOCOL);
return SendMessage(message);
}
int Ctcs::Send_Torrent(const unsigned char *peerid, char *torrent)
{
char message[CTCS_BUFSIZE];
char txtid[PEER_ID_LEN*2+3];
TextPeerID(peerid, txtid);
snprintf(message, CTCS_BUFSIZE, "CTORRENT %s %ld %ld %s", txtid,
(long)(BTCONTENT.GetStartTime()), (long)now, torrent);
return SendMessage(message);
}
int Ctcs::Report_Status()
{
int changebw;
size_t dlrate, ulrate, dlimit, ulimit;
if( T_READY != m_status ) return 0;
dlrate = Self.RateDL();
ulrate = Self.RateUL();
dlimit = cfg_max_bandwidth_down;
ulimit = cfg_max_bandwidth_up;
changebw = (
compset(m_ctstatus, dlrate) |
compset(m_ctstatus, ulrate) |
compset(m_ctstatus, dlimit) |
compset(m_ctstatus, ulimit) );
m_statustime = now;
if( !m_sent_ctstatus ||
(Tracker.GetStatus() && now > m_sent_ctstatus_time+30) )
return Send_Status();
else return (changebw || !m_sent_ctbw) ? Send_bw() : 0;
}
int Ctcs::Send_Status()
{
char message[CTCS_BUFSIZE];
size_t seeders, leechers, nhave, ntotal, dlrate, ulrate, dlimit, ulimit,
cacheused;
uint64_t dltotal, ultotal;
if( T_READY != m_status ) return 0;
seeders = WORLD.GetSeedsCount();
leechers = WORLD.GetPeersCount() - seeders - WORLD.GetConnCount();
nhave = BTCONTENT.pBF->Count();
ntotal = BTCONTENT.GetNPieces();
dlrate = m_ctstatus.dlrate;
ulrate = m_ctstatus.ulrate;
dltotal = Self.TotalDL();
ultotal = Self.TotalUL();
dlimit = cfg_max_bandwidth_down;
ulimit = cfg_max_bandwidth_up;
cacheused = BTCONTENT.CacheUsed()/1024;
if( m_protocol == 1 )
snprintf( message, CTCS_BUFSIZE,
"CTSTATUS %d/%d %d/%d/%d %d,%d %llu,%llu %d,%d",
(int)seeders, (int)leechers,
(int)nhave, (int)ntotal, (int)(WORLD.Pieces_I_Can_Get()),
(int)dlrate, (int)ulrate,
(unsigned long long)dltotal, (unsigned long long)ultotal,
(int)dlimit, (int)ulimit );
else
snprintf( message, CTCS_BUFSIZE,
"CTSTATUS %d:%d/%d:%d/%d %d/%d/%d %d,%d %llu,%llu %d,%d %d",
(int)seeders,
(int)(Tracker.GetSeedsCount()) - (BTCONTENT.IsFull() ? 1 : 0),
(int)leechers,
(int)(Tracker.GetPeersCount()) - Tracker.GetSeedsCount() -
(!BTCONTENT.IsFull() ? 1 : 0),
(int)(WORLD.GetConnCount()),
(int)nhave, (int)ntotal, (int)(WORLD.Pieces_I_Can_Get()),
(int)dlrate, (int)ulrate,
(unsigned long long)dltotal, (unsigned long long)ultotal,
(int)dlimit, (int)ulimit,
(int)cacheused );
m_sent_ctstatus = m_sent_ctbw = 1;
m_sent_ctstatus_time = now;
return SendMessage(message);
}
int Ctcs::Send_bw()
{
char message[CTCS_BUFSIZE];
snprintf(message, CTCS_BUFSIZE, "CTBW %d,%d %d,%d",
(int)(m_ctstatus.dlrate), (int)(m_ctstatus.ulrate),
(int)(m_ctstatus.dlimit), (int)(m_ctstatus.ulimit) );
m_sent_ctbw = 1;
return SendMessage(message);
}
int Ctcs::Send_Config()
{
char message[CTCS_BUFSIZE];
if( m_protocol >= 3 ){
int r = 0;
char value[MAXPATHLEN], desc[MAXPATHLEN], maxlen[10];
if( (r=SendMessage("CTCONFIGSTART")) < 0 ) return r;
snprintf(maxlen, sizeof(maxlen), "%u", (unsigned int)MAXPATHLEN);
if( (r = SendMessage(ConfigMsg("verbose", "B", "0", arg_verbose?"1":"0",
"Verbose output [-v]", arg_verbose ? "Enabled" : "Disabled"))) < 0 )
return r;
double num = BTCONTENT.GetSeedTime() ?
(cfg_seed_hours - (now - BTCONTENT.GetSeedTime())/3600.0) :
cfg_seed_hours;
unsigned long tmp = (unsigned long)(num * 100);
int pre = (tmp % 10) ? 2 : (tmp % 100) ? 1 : 0;
snprintf(value, MAXPATHLEN, "%.*f", pre, num);
snprintf(desc, MAXPATHLEN, "~hours remaining (-e %lu)",
(unsigned long)cfg_seed_hours);
if( (r = SendMessage(ConfigMsg("seed_time", "F", "0", value,
"Seed time [-e]", desc))) < 0 )
return r;
snprintf(value, MAXPATHLEN, "%.2f", (double)cfg_seed_ratio);
if( (r = SendMessage(ConfigMsg("seed_ratio", "F", "0", value,
"Seed ratio [-E]", "Upload:Download"))) < 0 )
return r;
snprintf(value, MAXPATHLEN, "%d", (int)cfg_max_peers);
snprintf(desc, MAXPATHLEN, "Current peers: %d",
(int)(WORLD.GetPeersCount()));
if( (r = SendMessage(ConfigMsg("max_peers", "I", "20-1000", value,
"Max peers [-M]", desc))) < 0 )
return r;
snprintf(value, MAXPATHLEN, "%d", (int)cfg_min_peers);
snprintf(desc, MAXPATHLEN, "Current peers: %d",
(int)(WORLD.GetPeersCount()));
if( (r = SendMessage(ConfigMsg("min_peers", "I", "1-1000", value,
"Min peers [-m]", desc))) < 0 )
return r;
if( !BTCONTENT.IsFull() &&
(r = SendMessage(ConfigMsg("file_list", "S", maxlen,
arg_file_to_download ? arg_file_to_download : "",
"Download files [-n]", ""))) < 0 )
return r;
snprintf(value, MAXPATHLEN, "%d", (int)cfg_cache_size);
snprintf(desc, MAXPATHLEN, "MB; %dKB now in use",
(int)(BTCONTENT.CacheUsed()/1024));
if( (r = SendMessage(ConfigMsg("cache", "I", "0", value,
"Cache size [-C]", desc))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("pause", "B", "0",
WORLD.IsPaused()?"1":"0",
"Pause torrent", "Stop upload/download"))) < 0 )
return r;
if( !BTCONTENT.IsFull() &&
(r = SendMessage(ConfigMsg("user_exit", "S", maxlen,
arg_completion_exit ? arg_completion_exit : "",
"Completion command [-X]", ""))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("out_normal", "S", maxlen,
CONSOLE.GetChannel(O_NORMAL), "Normal/status output", ""))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("out_interact", "S", maxlen,
CONSOLE.GetChannel(O_INTERACT), "Interactive output", ""))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("out_error", "S", maxlen,
CONSOLE.GetChannel(O_WARNING), "Error/warning output", ""))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("out_debug", "S", maxlen,
CONSOLE.GetChannel(O_DEBUG), "Debug/verbose output", ""))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("input", "S", maxlen,
CONSOLE.GetChannel(O_INPUT), "Console input", ""))) < 0 )
return r;
if( (r = SendMessage(ConfigMsg("ctcs_server", "S", maxlen,
arg_ctcs, "CTCS server", ""))) < 0 )
return r;
sprintf(message, "CTCONFIGDONE");
}
else if( m_protocol == 2 )
snprintf(message, CTCS_BUFSIZE, "CTCONFIG %d %d %f %d %d %d %d %d",
(int)arg_verbose, (int)cfg_seed_hours, cfg_seed_ratio,
(int)cfg_max_peers, (int)cfg_min_peers,
BTCONTENT.GetFilter() ? atoi(BTCONTENT.GetFilterName()) : 0,
(int)cfg_cache_size, WORLD.IsPaused());
else // m_protocol == 1
snprintf(message, CTCS_BUFSIZE, "CTCONFIG %d %d %f %d %d %d %d %d %d",
(int)arg_verbose, (int)cfg_seed_hours, cfg_seed_ratio,
(int)cfg_max_peers, (int)cfg_min_peers,
BTCONTENT.GetFilter() ? atoi(BTCONTENT.GetFilterName()) : 0,
0, WORLD.IsPaused(), 0);
return SendMessage(message);
}
char *Ctcs::ConfigMsg(const char *name, const char *type, const char *range,
const char *value, const char *short_desc, const char *long_desc)
{
static char *message = (char *)0;
if( !message ){
message = new char[CTCS_BUFSIZE];
if( !message ){
CONSOLE.Warning(1, "error, failed to allocate memory for CTCS message");
return (char *)0;
}
}
snprintf(message, CTCS_BUFSIZE, "CTCONFIG %s %s %s %d:%s %d:%s %d:%s",
name, type, range, (int)strlen(value), value,
(int)strlen(short_desc), short_desc, (int)strlen(long_desc), long_desc);
return message;
}
int Ctcs::Set_Config(const char *origmsg)
{
char *msgbuf = new char[strlen(origmsg)+1];
if( !msgbuf ){
CONSOLE.Warning(1, "error, failed to allocate memory for config");
return -1;
}
strcpy(msgbuf, origmsg);
if( m_protocol >= 3 ){
char *name, *valstr;
if( !(name = strtok(strchr(msgbuf, ' '), " ")) ||
strlen(name) >= strlen(origmsg) - (name - msgbuf) )
goto err;
valstr = name + strlen(name);
for( ++valstr; *valstr==' '; valstr++ );
if( 0==strcmp(name, "verbose") ){
int arg = atoi(valstr);
if( arg < 0 ) goto err;
if( arg_verbose && !arg ) CONSOLE.Debug("Verbose output off");
arg_verbose = arg;
}else if( 0==strcmp(name, "seed_time") ){
double value = strtod(valstr, NULL);
if( value < 0 ) goto err;
time_t arg = (time_t)value + ((value - (int)value) ? 1 : 0);
arg += BTCONTENT.GetSeedTime() ?
((now - BTCONTENT.GetSeedTime()) / 3600) : 0;
if( arg > 0 || 0==BTCONTENT.GetSeedTime() ||
cfg_seed_ratio > (double) Self.TotalUL() /
(Self.TotalDL() ?
Self.TotalDL() : BTCONTENT.GetTotalFilesLength()) )
cfg_seed_hours = arg;
}else if( 0==strcmp(name, "seed_ratio") ){
double arg = atof(valstr);
if( arg < 0 ) goto err;
if( 0==BTCONTENT.GetSeedTime() ||
cfg_seed_hours > (now - BTCONTENT.GetSeedTime()) / 3600 ||
arg > (double) Self.TotalUL() /
(Self.TotalDL() ?
Self.TotalDL() : BTCONTENT.GetTotalFilesLength()) )
cfg_seed_ratio = arg;
}else if( 0==strcmp(name, "max_peers") ){
int arg = atoi(valstr);
if( arg > 1000 || arg < 20 ) goto err;
cfg_max_peers = arg;
}else if( 0==strcmp(name, "min_peers") ){
int arg = atoi(valstr);
if( arg > 1000 || arg < 1 ) goto err;
cfg_min_peers = arg;
}else if( 0==strcmp(name, "file_list") ){
if( !BTCONTENT.IsFull() ){
if( arg_file_to_download ) delete []arg_file_to_download;
if( 0==strlen(valstr) ) arg_file_to_download = (char *)0;
else{
arg_file_to_download = new char[strlen(valstr) + 1];
if( !arg_file_to_download )
CONSOLE.Warning(1, "error, failed to allocate memory for option");
else strcpy(arg_file_to_download, valstr);
}
BTCONTENT.SetFilter();
}
}else if( 0==strcmp(name, "cache") ){
int arg = atoi(valstr);
if( arg < 0 ) goto err;
cfg_cache_size = arg;
BTCONTENT.CacheConfigure();
}else if( 0==strcmp(name, "pause") ){
int arg = atoi(valstr);
if( arg < 0 ) goto err;
if( arg ){
if( !WORLD.IsPaused() ) WORLD.Pause();
}else if( WORLD.IsPaused() ) WORLD.Resume();
}else if( 0==strcmp(name, "user_exit") ){
if( !BTCONTENT.IsFull() ){
if( arg_completion_exit ) delete []arg_completion_exit;
arg_completion_exit = new char[strlen(valstr) + 1];
if( !arg_completion_exit )
CONSOLE.Warning(1, "error, failed to allocate memory for option");
else strcpy(arg_completion_exit, valstr);
}
}else if( 0==strcmp(name, "out_normal") ){
CONSOLE.ChangeChannel(O_NORMAL, valstr);
}else if( 0==strcmp(name, "out_interact") ){
CONSOLE.ChangeChannel(O_INTERACT, valstr);
}else if( 0==strcmp(name, "out_error") ){
CONSOLE.ChangeChannel(O_WARNING, valstr);
}else if( 0==strcmp(name, "out_debug") ){
CONSOLE.ChangeChannel(O_DEBUG, valstr);
}else if( 0==strcmp(name, "input") ){
CONSOLE.ChangeChannel(O_INPUT, valstr);
}else if( 0==strcmp(name, "ctcs_server") ){
if( !strchr(valstr, ':') || *valstr == ':' ||
atoi(strchr(valstr, ':') + 1) <= 0 ) goto err;
char *arg = new char[strlen(valstr) + 1];
if( !arg )
CONSOLE.Warning(1, "error, failed to allocate memory for option");
else{
strcpy(arg, valstr);
delete []arg_ctcs;
arg_ctcs = arg;
CTCS.Initial();
CTCS.Reset(1);
}
}else CONSOLE.Warning(2, "Unknown config option %s from CTCS", name);
}else{ // m_protocol <= 2
if(msgbuf[9] != '.'){
int arg = atoi(msgbuf+9);
if( arg_verbose && !arg ) CONSOLE.Debug("Verbose output off");
arg_verbose = arg;
}
if(msgbuf[11] != '.') cfg_seed_hours = atoi(msgbuf+11);
if( !(msgbuf = strchr(msgbuf+11, ' ')) ) goto err;
if(*++msgbuf != '.') cfg_seed_ratio = atof(msgbuf);
if( !(msgbuf = strchr(msgbuf, ' ')) ) goto err;
if(*++msgbuf != '.') cfg_max_peers = atoi(msgbuf);
if( !(msgbuf = strchr(msgbuf, ' ')) ) goto err;
if(*++msgbuf != '.') cfg_min_peers = atoi(msgbuf);
if( !(msgbuf = strchr(msgbuf, ' ')) ) goto err;
if(*++msgbuf != '.'){
char *p = strchr(msgbuf, ' ');
if( !p ) goto err;
if( arg_file_to_download ) delete []arg_file_to_download;
arg_file_to_download = new char[p - msgbuf + 2 + 1];
if( !arg_file_to_download )
CONSOLE.Warning(1, "error, failed to allocate memory for option");
else{
strncpy(arg_file_to_download, msgbuf, p - msgbuf);
arg_file_to_download[p - msgbuf] = '\0';
strcat(arg_file_to_download, ",*"); // mock old behavior
}
BTCONTENT.SetFilter();
}
if( m_protocol >= 2 ){
if( !(msgbuf = strchr(msgbuf, ' ')) ) goto err;
if(*++msgbuf != '.'){
cfg_cache_size = atoi(msgbuf);
BTCONTENT.CacheConfigure();
}
}
if( m_protocol == 1 ){
if( !(msgbuf = strchr(msgbuf, ' ')) ) goto err;
++msgbuf;
// old cfg_exit_zero_peers option
}
if( !(msgbuf = strchr(msgbuf, ' ')) ) goto err;
if(*++msgbuf != '.'){
if(atoi(msgbuf)){
if( !WORLD.IsPaused() ) WORLD.Pause();
}else if( WORLD.IsPaused() ) WORLD.Resume();
}
}
delete []msgbuf;
return 0;
err:
CONSOLE.Warning(2, "Malformed or invalid input from CTCS: %s", origmsg);
delete []msgbuf;
return -1;
}
int Ctcs::Send_Detail()
{
char message[CTCS_BUFSIZE];
int r=0, priority, current=0;
size_t n=0;
BTFILE *file=0;
BitField tmpBitField, fileFilter, availbf, tmpavail, allFilter, tmpFilter,
*pfilter;
snprintf( message, CTCS_BUFSIZE, "CTDETAIL %lld %d %ld %ld",
BTCONTENT.GetTotalFilesLength(),
(int)(BTCONTENT.GetPieceLength()), (long)now,
(long)(BTCONTENT.GetSeedTime()) );
r = SendMessage(message);
if(r==0) r = SendMessage((m_protocol >= 3) ? "CTFILESTART" : "CTFILES");
if( m_protocol >= 3 ){ // determine current download priority
pfilter = (BitField *)0;
while( pfilter != BTCONTENT.GetFilter() ){
current++;
pfilter = BTCONTENT.GetNextFilter(pfilter);
}
}
WORLD.Pieces_I_Can_Get(&availbf);
while( r==0 && ++n <= BTCONTENT.GetNFiles() ){
tmpBitField = *BTCONTENT.pBF;
BTCONTENT.SetTmpFilter(n, &fileFilter);
tmpBitField.Except(fileFilter); // the pieces of this file that I have
tmpavail = availbf;
tmpavail.Except(fileFilter); // the available pieces of this file
if( m_protocol >= 3 ){
priority = 0;
if( BTCONTENT.GetFilter() ){
fileFilter.Invert();
allFilter.SetAll();
pfilter = (BitField *)0;
while( pfilter = BTCONTENT.GetNextFilter(pfilter) ){
priority++;
allFilter.And(*pfilter); // cumulation of filters
tmpFilter = allFilter;
tmpFilter.Invert(); // what's included by the filters...
tmpFilter.And(fileFilter); // ...that's also in this file
if( tmpFilter.Count() >= fileFilter.Count() ) break;
}
if( !pfilter ) priority = 0;
}
snprintf( message, CTCS_BUFSIZE, "CTFILE %d %d %d %d %d %d %llu %s",
(int)n, priority, current, (int)(BTCONTENT.GetFilePieces(n)),
(int)(tmpBitField.Count()), (int)(tmpavail.Count()),
(unsigned long long)(BTCONTENT.GetFileSize(n)),
BTCONTENT.GetFileName(n) );
}
else if( m_protocol == 2 )
snprintf( message, CTCS_BUFSIZE, "CTFILE %d %d %d %d %llu %s",
(int)n, (int)(BTCONTENT.GetFilePieces(n)),
(int)(tmpBitField.Count()), (int)(tmpavail.Count()),
(unsigned long long)(BTCONTENT.GetFileSize(n)),
BTCONTENT.GetFileName(n) );
else // m_protocol == 1
snprintf( message, CTCS_BUFSIZE, "CTFILE %d %d %d %llu %s",
(int)n, (int)(BTCONTENT.GetFilePieces(n)),
(int)(tmpBitField.Count()),
(unsigned long long)(BTCONTENT.GetFileSize(n)),
BTCONTENT.GetFileName(n) );
r = SendMessage(message);
}
if(r==0) r = SendMessage((m_protocol >= 3) ? "CTFILESDONE" : "CTFDONE");
return r;
}
int Ctcs::Send_Peers()
{
btPeer *peer=0;
char message[CTCS_BUFSIZE];
char txtid[PEER_ID_LEN*2+3];
struct sockaddr_in psin;
int r=0;
r=SendMessage((m_protocol >= 3) ? "CTPEERSTART" : "CTPEERS");
while( r==0 && (peer = WORLD.GetNextPeer(peer)) ){
TextPeerID(peer->id, txtid);
peer->GetAddress(&psin);
snprintf(message, CTCS_BUFSIZE, "CTPEER %s %s %c%c%c%c %d %d %llu %llu %d",
txtid, inet_ntoa(psin.sin_addr),
peer->Is_Remote_UnChoked() ? 'U' : 'C',
peer->Is_Local_Interested() ? 'i' : 'n',
peer->Is_Local_UnChoked() ? 'U' : 'C',
peer->Is_Remote_Interested() ? 'i' : 'n',
(int)(peer->RateDL()), (int)(peer->RateUL()),
(unsigned long long)(peer->TotalDL()),
(unsigned long long)(peer->TotalUL()),
(int)(peer->bitfield.Count()) );
r = SendMessage(message);
}
if(r==0) r = SendMessage((m_protocol >= 3) ? "CTPEERSDONE" : "CTPDONE");
return r;
}
int Ctcs::Send_Info(int sev, const char *info)
{
char message[CTCS_BUFSIZE];
snprintf(message, CTCS_BUFSIZE, "CTINFO %d %s", sev, info);
return SendMessage(message);
}
int Ctcs::Initial()
{
char *s;
strncpy(m_host, arg_ctcs, MAXHOSTNAMELEN-1);
m_host[MAXHOSTNAMELEN-1] = '\0';
if( s = strchr(m_host, ':') ) *s='\0';
m_port = atoi(s=(strchr(arg_ctcs, ':')+1));
if(strchr(s, ':')){
CONSOLE.Input("Enter CTCS password: ", m_pass, CTCS_PASS_SIZE);
} else *m_pass = '\0';
return 0;
}
int Ctcs::Connect()
{
ssize_t r;
m_last_timestamp = now;
if(_s2sin(m_host,m_port,&m_sin) < 0) {
CONSOLE.Warning(2, "warn, get CTCS ip address failed.");
return -1;
}
m_sock = socket(AF_INET,SOCK_STREAM,0);
if( INVALID_SOCKET == m_sock ) return -1;
if( setfd_nonblock(m_sock) < 0 ){ CLOSE_SOCKET(m_sock); return -1; }
r = connect_nonb(m_sock,(struct sockaddr*)&m_sin);
if( r == -1 ){ CLOSE_SOCKET(m_sock); return -1; }
else if( r == -2 ) m_status = T_CONNECTING;
else{
m_status = T_READY;
if(arg_verbose) CONSOLE.Debug("Connected to CTCS");
if( Send_Protocol() != 0 && errno != EINPROGRESS ){
CONSOLE.Warning(2, "warn, send protocol to CTCS failed: %s",
strerror(errno));
return -1;
}
if( Send_Auth() != 0 && errno != EINPROGRESS ) {
CONSOLE.Warning(2, "warn, send password to CTCS failed: %s",
strerror(errno));
return -1;
}
if( Send_Torrent(BTCONTENT.GetPeerId(), arg_metainfo_file) < 0 &&
errno != EINPROGRESS ){
CONSOLE.Warning(2, "warn, send torrent to CTCS failed: %s",
strerror(errno));
return -1;
}
}
return 0;
}
int Ctcs::IntervalCheck(fd_set *rfdp, fd_set *wfdp)
{
if( T_FREE == m_status ){
if( INVALID_SOCKET != m_sock ){
FD_CLR(m_sock, rfdp);
FD_CLR(m_sock, wfdp);
}
if( now - m_last_timestamp >= m_interval ){
if(Connect() < 0){ Reset(15); return -1; }
FD_SET(m_sock, rfdp);
if( m_status == T_CONNECTING ) FD_SET(m_sock, wfdp);
}else if( now < m_last_timestamp ) m_last_timestamp = now;
}else if( T_CONNECTING == m_status ){
FD_SET(m_sock, rfdp);
FD_SET(m_sock, wfdp);
}else if( INVALID_SOCKET != m_sock ){
if( now > m_statustime ) Report_Status();
FD_SET(m_sock, rfdp);
if( out_buffer.Count() ) FD_SET(m_sock, wfdp);
}
return m_sock;
}
int Ctcs::SocketReady(fd_set *rfdp, fd_set *wfdp, int *nfds,
fd_set *rfdnextp, fd_set *wfdnextp)
{
if( T_FREE == m_status ) return 0;
if( T_CONNECTING == m_status && FD_ISSET(m_sock,wfdp) ){
int error = 0;
socklen_t n = sizeof(error);
(*nfds)--;
FD_CLR(m_sock, wfdnextp);
if( FD_ISSET(m_sock, rfdp) ){
(*nfds)--;
FD_CLR(m_sock, rfdnextp);
}
if(getsockopt(m_sock, SOL_SOCKET,SO_ERROR,&error,&n) < 0)
error = errno;
if( error != 0 ){
if( ECONNREFUSED != error )
CONSOLE.Warning(2, "warn, connect to CTCS failed: %s",
strerror(error));
Reset(15);
return -1;
}else{
m_status = T_READY;
if(arg_verbose) CONSOLE.Debug("Connected to CTCS");
if( Send_Protocol() != 0 && errno != EINPROGRESS ){
CONSOLE.Warning(2, "warn, send protocol to CTCS failed: %s",
strerror(errno));
return -1;
}
if( Send_Auth() != 0 && errno != EINPROGRESS ) {
CONSOLE.Warning(2, "warn, send password to CTCS failed: %s",
strerror(errno));
return -1;
}
if( Send_Torrent(BTCONTENT.GetPeerId(), arg_metainfo_file) < 0 &&
errno != EINPROGRESS ){
CONSOLE.Warning(2, "warn, send torrent to CTCS failed: %s",
strerror(errno));
return -1;
}
}
}else if( T_CONNECTING == m_status && FD_ISSET(m_sock,rfdp) ){
int error = 0;
socklen_t n = sizeof(error);
(*nfds)--;
FD_CLR(m_sock, rfdnextp);
if(getsockopt(m_sock, SOL_SOCKET,SO_ERROR,&error,&n) < 0)
error = errno;
CONSOLE.Warning(2, "warn, connect to CTCS failed: %s", strerror(error));
Reset(15);
return -1;
}else if( INVALID_SOCKET != m_sock ){
if( FD_ISSET(m_sock, rfdp) ){
(*nfds)--;
FD_CLR(m_sock,rfdnextp);
SOCKET tmp_sock = m_sock;
int r = CheckMessage();
if( INVALID_SOCKET == m_sock ){
if( FD_ISSET(tmp_sock, wfdp) ){
(*nfds)--;
FD_CLR(tmp_sock,wfdnextp);
}
return r;
}
}
if( FD_ISSET(m_sock, wfdp) ){
(*nfds)--;
FD_CLR(m_sock,wfdnextp);
if( out_buffer.Count() && out_buffer.FlushOut(m_sock) < 0 ){
Reset(5);
return -1;
}
}
}else{ // failsafe
Reset(5);
return -1;
}
return 0;
}