/*	WebDownloader for X-Window
 *	Copyright (C) 1999-2002 Koshelev Maxim
 *	This Program is free but not GPL!!! You can't modify it
 *	without agreement with author. You can't distribute modified
 *	program but you can distribute unmodified program.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <sys/types.h>
#include <sys/stat.h>

#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif

#include <sys/timeb.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <signal.h>
#include "var.h"
#include "ftpd.h"
#include "httpd.h"
#include "hproxy.h"
#include "locstr.h"
#include "main.h"
#include "dlist.h"
#include "meter.h"
#include "log.h"
#include "mainlog.h"
#include "signal.h"
#include "savelog.h"
#include "face/list.h"
#include "face/buttons.h"
#include "face/log.h"
#include "face/edit.h"
#include "face/addd.h"
#include "face/fsface.h"
#include "config.h"
#include "ntlocale.h"
#include "memwl.h"
#include "schedule.h"
#include "html.h"
#include "filter.h"
#include "sndserv.h"
#include "xml.h"
#include "face/passface.h"
#include "face/saveload.h"
#include <algorithm>
#include <functional>

tMLog *MainLog=NULL;

d4xDownloadQueue *D4X_QUEUE=NULL;
tQueue D4X_QTREE;
tMeter *GlobalMeter=NULL;
tMeter *LocalMeter=NULL;
tMeter *GraphMeter=NULL;
tMeter *GraphLMeter=NULL;
tMsgQueue *LogsMsgQueue;

int calc_curent_run(char *host,int port) {
	return (D4X_QUEUE->current_run(host,port));
};

int d4x_only_one_queue(){
	if (D4X_QTREE.count()>1 || ((d4xDownloadQueue*)(D4X_QTREE.first()))->child.count()) return(0);
	return(1);
};

static void d4x_qtree_for_each_rec(tQueue *q,d4xQTreeFunc dothis,void *a){
	d4xDownloadQueue *dq=(d4xDownloadQueue*)(q->first());
	while (dq){
		d4xDownloadQueue *next=(d4xDownloadQueue *)(dq->prev);
		d4x_qtree_for_each_rec(&(dq->child),dothis,a);
		dothis(dq,a);
		dq=next;
	};
};

void d4x_qtree_for_each(d4xQTreeFunc dothis,void *a){
	d4x_qtree_for_each_rec(&D4X_QTREE,dothis,a);
};

//**********************************************/

typedef void (*SigactionHandler)(int);

void _sig_pipe_handler_(){
};

int tMain::init() {
	TO_WAIT_IF_HERE=DONTRY2RUN=0;
	GVARS.SOCKETS=new d4x::SocketsHistory;
	ftpsearch=NULL;
	prev_speed_limit=0;
	list_to_delete=NULL;
	GlobalMeter=new tMeter;
	GlobalMeter->init(METER_LENGTH);
	LocalMeter=new tMeter;
	LocalMeter->init(METER_LENGTH);
	GraphMeter=new tMeter;
	GraphMeter->init(GRAPH_METER_LENGTH);
	GraphLMeter=new tMeter;
	GraphLMeter->init(GRAPH_METER_LENGTH);
	
	SpeedScheduler=new d4x::SpeedQueue;
	MainScheduler=new d4xScheduler;
	MainScheduler->load();

	ALL_DOWNLOADS=new tDB;
	d4x::filters_load_rc();

	LastReadedBytes=0;
	/* Create msgqueue for logs update */
	MsgQueue=new tMsgQueue;
	MsgQueue->init(0);
	LogsMsgQueue=MsgQueue;

	SOUND_SERVER=new d4xSndServer;
	SOUND_SERVER->init_sounds();

	/* setting up signal handlers */
	struct sigaction action,old_action;
	action.sa_handler=SigactionHandler(my_main_quit);
	action.sa_flags=0;//SA_NOCLDSTOP;
	sigaction(SIGINT,&action,&old_action);
	sigaction(SIGTERM,&action,&old_action);
	action.sa_flags=0;
	action.sa_handler=SigactionHandler(_sig_pipe_handler_);
	sigaction(SIGPIPE,&action,&old_action);
	sigset_t oldmask,newmask;
	sigemptyset(&newmask);
	sigaddset(&newmask,SIGINT);
	sigaddset(&newmask,SIGTERM);
	pthread_sigmask(SIG_UNBLOCK,&newmask,&oldmask);
	server=new tMsgServer;
	if (server->init()){
		perror(_("Can't init control socket!\n"));
		delete(server);
		return(-1);
	};
	FaceForPasswords=new tFacePass;
	FaceForPasswords->load();
	return(0);
};

void create_new_queue(char *name,d4xDownloadQueue *papa){
	d4xDownloadQueue *q=new d4xDownloadQueue;
	q->name.set(name);
	if (papa){
		q->inherit_settings(papa);
		papa->subq_add(q);
	}else{
		if (D4X_QTREE.last())
			q->inherit_settings((d4xDownloadQueue*)D4X_QTREE.last(),CFG.GLOBAL_SAVE_PATH);
		D4X_QTREE.insert(q);
	};
	if (CFG.WITHOUT_FACE==0){
		q->qv.init();
		q->init_pixmaps();
		D4X_QVT->add(q,papa);
		if (!D4X_QUEUE) D4X_QVT->switch_remote(q);
	}else{
		if (!D4X_QUEUE) D4X_QUEUE=q;
	};
};

void tMain::init_qtree(tQueue *list,d4xDownloadQueue *papa){
	d4xDownloadQueue *q=(d4xDownloadQueue *)(list->first());
	while(q){
		if (q->SpdLmt)
			SpeedScheduler->insert(q);
		q->parent=papa;
		if (CFG.WITHOUT_FACE==0){
			D4X_QVT->add(q,papa);
			if (q->IamDefault)
				D4X_QVT->switch_remote(q);
		}else{
			if (q->IamDefault){
				D4X_QUEUE=q;
			};
		};
		init_qtree(&(q->child),q);
		try_to_run_run(q);
		try_to_run_wait(q);
		q=(d4xDownloadQueue *)(q->prev);
	};
};

void tMain::load_defaults() {
	MainLog->add(_("Loading default list of downloads"),LOG_OK|LOG_DETAILED);
	read_list();
	if (D4X_QTREE.count()){
		init_qtree(&D4X_QTREE);
		if (!D4X_QUEUE){
			d4xDownloadQueue *q=(d4xDownloadQueue *)(D4X_QTREE.first());
			if (CFG.WITHOUT_FACE){
				D4X_QUEUE=q;
			}else{
				D4X_QVT->switch_remote(q);
			};
		};
	}else{
		create_new_queue("Main");
	};
	if (!CFG.WITHOUT_FACE)
		D4X_QUEUE->qv.set_shift(CFG.CLIST_SHIFT);
};


void tMain::init_main_log() {
	MainLog=new tMLog;
	MainLog->init(CFG.MAX_MAIN_LOG_LENGTH);
	if (CFG.WITHOUT_FACE==0)
		MainLog->init_list(GTK_TREE_VIEW(MainLogList));
	MainLog->reinit_file();
	MainLog->add("----------------------------------------",LOG_FROM_SERVER);
};

void tMain::redraw_logs() {
	int limit = 12;
	GList *tmplist=NULL;
	while (limit>0) {
		limit--;
		tLogMsg *Msg=(tLogMsg*)MsgQueue->first();
		if (Msg) {
			if (Msg->type&MQT_MY){
				if (Msg->what){
					if (Msg->which->freezed_flag==0){
						tmplist=log_window_freeze(tmplist,Msg->which);
					};
					log_window_add_string(Msg->which,Msg->what);
				}else{
					del_first_from_log(Msg->which);
				};
			};
			if (Msg->type&MQT_COM){
				if (Msg->what){
					if (Msg->which==D4X_LOG_DISPLAY.log)
						d4x_main_log_add_string(Msg->what);
				}else{
					if (Msg->which==D4X_LOG_DISPLAY.log)
						d4x_main_log_del_string();
				};
			};
			Msg->which->unlock();
			Msg->which->ref_dec();
			MsgQueue->del(Msg);
			delete(Msg);
		}else
			break;
	};
	while(tmplist){
		tmplist=log_window_unfreeze(tmplist);
	};
};


void tMain::absolute_delete_download(tDownload *what) {
	DBC_RETURN_IF_FAIL(what!=NULL);
	if (CFG.WITHOUT_FACE==0 && D4X_LOG_DISPLAY.papa==what)
		D4X_LOG_DISPLAY.papa=NULL;
	DQV(what).remove(what);
	d4xDownloadQueue *q=what->myowner->PAPA;
	q->del(what);
	ALL_DOWNLOADS->del(what);
	FaceForPasswords->stop_matched(what);
	delete(what);
};


void tMain::del_completed(d4xDownloadQueue *queue) {
	MainLog->add(_("Delete completed downloads"),LOG_OK|LOG_DETAILED);
	del_all_from_list(DL_COMPLETE,queue);
};

void tMain::rerun_failed(){
	tDownload *temp=D4X_QUEUE->first(DL_STOP);
	while (temp) {
		continue_download(temp);
		temp=D4X_QUEUE->first(DL_STOP);
	};
};

void tMain::del_fataled(d4xDownloadQueue *queue){
	MainLog->add(_("Delete failed downloads"),LOG_OK|LOG_DETAILED);
	del_all_from_list(DL_STOP,queue);
};

void tMain::del_all_from_list(int list,d4xDownloadQueue *queue){
	tDownload *temp=queue?queue->first(list):D4X_QUEUE->first(list);
	while (temp) {
		tDownload *next=(tDownload *)(temp->prev);
		if (!temp->protect){
			absolute_delete_download(temp);
		};
		temp=next;
	};
};

void tMain::del_all() {
	if (D4X_QUEUE->count())
		MainLog->add(_("Clear queue of downloads"),LOG_ERROR);
	tDownload *temp=D4X_QUEUE->first(DL_RUN);
	while (temp) {
		stop_download(temp);
		temp=D4X_QUEUE->first(DL_RUN);
	};
	temp=D4X_QUEUE->first(DL_STOPWAIT);
	while (temp) {
		if (!temp->protect)
			temp->action=ACTION_DELETE;
		temp=(tDownload *)(temp->prev);
	};
	del_all_from_list(DL_PAUSE);
	del_all_from_list(DL_WAIT);
	del_all_from_list(DL_STOP);
	del_all_from_list(DL_COMPLETE);
	del_all_from_list(DL_LIMIT);
};


int tMain::run_new_thread(tDownload *what) {
	DBC_RETVAL_IF_FAIL(what!=NULL,-1);
	pthread_attr_t attr_p;
	what->status=READY_TO_RUN;
	what->STOPPED_BY_USER=false;
	if (!what->LOG) {
		what->LOG=new tLog;
		what->LOG->ref_inc();
		if (CFG.WITHOUT_FACE)
			what->LOG->init(2); // two strings in log if run without interface
		else
			what->LOG->init(CFG.MAX_LOG_LENGTH);
	};
	if (what->info.proto==D_PROTO_SEARCH){
		what->WL=new tMemoryWL;
		((tMemoryWL *)(what->WL))->set_log(what->LOG);
	}else{
		what->WL=new tDefaultWL;
		((tDefaultWL *)(what->WL))->set_log(what->LOG);
	};
	what->update_trigers();
	what->config->redirect_count=0;
	what->Size.reset(); // no need update size

	if (what->SpeedLimit==NULL) what->SpeedLimit=new d4x::Speed;
/* set speed limitation */
	if (what->split && what->split->NumOfParts){
		what->SpeedLimit->base = what->config->speed/what->split->NumOfParts;
	}else{
		what->SpeedLimit->base = what->config->speed;
	};
	what->set_initial_speedlimit();
	SpeedScheduler->insert(what->SpeedLimit);
	if (what->editor) what->editor->disable_ok_button();

	MainLog->myprintf(LOG_OK|LOG_DETAILED,_("Run new thread for %z"),what);

	pthread_attr_init(&attr_p);
	pthread_attr_setdetachstate(&attr_p,PTHREAD_CREATE_JOINABLE);
	pthread_attr_setscope(&attr_p,PTHREAD_SCOPE_SYSTEM);
	if (pthread_create(&what->thread_id,&attr_p,download_last,(void *)what)){
		MainLog->add(_("Can't run new thread for downloading!"),LOG_ERROR);
		return(-1);
	};
	return(0);
};

void tMain::stop_split(tDownload *what){
	DBC_RETURN_IF_FAIL(what!=NULL);
	tDownload *tmp=what->split->next_part;
	int a=1;
	while(tmp){
		a++;
		if (tmp->split->run)
			stop_thread(tmp);
		else
			what->split->stopcount+=1;
		tmp=tmp->split?tmp->split->next_part:NULL;
	};
	what->split->stopcount+=what->split->NumOfParts-a;
};

tDownload *tMain::find_url(const d4x::URL &adr){
	tDownload tmp;
	tmp.info=adr;
	tDownload *a=ALL_DOWNLOADS->find(&tmp);
	return(a);
};

void tMain::stop_download_url(const d4x::URL &adr){
	tDownload *a=find_url(adr);
	if (a) stop_download(a);
};

void tMain::continue_download_url(const d4x::URL &adr){
	tDownload *a=find_url(adr);
	if (a) continue_download(a);
};

void tMain::delete_download_url(const d4x::URL &adr){
	tDownload *a=find_url(adr);
	if (a) delete_download(a);
};

void tMain::stop_download(tDownload *what) {
	DBC_RETURN_IF_FAIL(what!=NULL);
	int owner=what->owner();
	if (owner==DL_STOPWAIT && what->action!=ACTION_DELETE) {
		what->action=ACTION_STOP;
		return;
	};
	d4xDownloadQueue *papa=what->myowner->PAPA;
	if (papa->is_first(DL_SIZEQUERY,what)){
		stop_thread(what);
		return;
	};
	if (owner==DL_RUN) {
		papa->del(what);
		MainLog->myprintf(LOG_WARNING,_("Downloading of file %s from %s was terminated [by user]"),
				  what->info.file.c_str(),
				  what->info.host.c_str());
		stop_thread(what);
		if (what->split)
			stop_split(what);
		papa->add(what,DL_STOPWAIT);
		what->ActStatus.clear();
	} else {
		if (owner==DL_WAIT || owner==DL_LIMIT || owner==DL_SIZEQUERY) {
			what->sizequery=0;
			FaceForPasswords->stop_matched(what);
			papa->del(what);
			papa->add(what,DL_PAUSE);
			what->ActStatus.clear();
			try_to_run_wait(papa);
		};
	};
};

int tMain::delete_download(tDownload *what,int flag) {
	if (!what) return 0;
	if (what->protect) return 0;
	if (what->owner()==DL_RUN)
		stop_download(what);
	d4xDownloadQueue *papa=what->myowner->PAPA;
	if (papa->is_first(DL_SIZEQUERY,what)){
		papa->del(what);
		stop_thread(what);
		papa->add(what,DL_STOPWAIT);
		what->action=ACTION_DELETE;
		sizequery_run_first(papa);
		return(0);
	};
	if (what->owner()==DL_STOPWAIT){
		if (flag)
			what->action=ACTION_REAL_DELETE;
		else
			what->action=ACTION_DELETE;
		return 0;
	};
	MainLog->myprintf(LOG_WARNING,_("Delete file %s from queue of downloads"),what->info.file.c_str());
	if (flag)
		what->remove_tmp_files();
	absolute_delete_download(what);
	return 1;
};

void tMain::try_to_run_split(tDownload *what){
	DBC_RETURN_IF_FAIL(what!=NULL);
	if (what->split->runcount>=what->split->NumOfParts) return;
	if (what->status==DOWNLOAD_GO || what->status==DOWNLOAD_COMPLETE){
		tDownload *dwn=what->split->next_part;
		while (dwn && FaceForPasswords->limit_check(what)==0){
			if (dwn->split->run==0){
				if (run_new_thread(dwn)) return;
				dwn->split->run=1;
				what->split->runcount+=1;
				FaceForPasswords->limit_inc(what);
			};
			dwn=dwn->split->next_part;
		};
	};
};

int tMain::try_to_run_download(tDownload *what){
	DBC_RETVAL_IF_FAIL(what!=NULL,-1);
	time_t NOW;
	time(&NOW);
	d4xDownloadQueue *papa=what->myowner->PAPA;
	if (papa->count(DL_RUN)<50) {
		what->SpeedCalc.reset();
		what->Start=what->Pause=time(NULL);
		if (what->config==NULL){
			what->config=new tCfg;
			what->set_default_cfg();
		};
		// to avoid old info in columns
		if (CFG.WITHOUT_FACE==0)
			DQV(what).change_data(what->list_iter,PAUSE_COL);
		if (what->split){
			what->finfo.size=-1;
			what->split->FirstByte=0;
			what->split->LastByte=-1;
			what->split->reset();
			what->config->rollback=0;
//			what->config->ftp_recurse_depth = 1;
			what->split->grandparent=what;
//			what->config->http_recurse_depth = 1;
		};
		if (run_new_thread(what)) return -1;
		if (what->split){
			what->split->runcount=1;
			what->split->run=1;
		};
		return 1;
	};
	return 0;
};

void tMain::insert_into_wait_list(tDownload *what,
				  d4xDownloadQueue *dq){
	if (CFG.WITHOUT_FACE){
		dq->add(what);
	}else{
		tDownload *temp=dq->last(DL_WAIT);
		if (!temp || dq->qv.get_row_num(temp) < dq->qv.get_row_num(what))
			dq->add(what);
		else {
			temp=dq->first(DL_WAIT);
			while (temp && dq->qv.get_row_num(temp) < dq->qv.get_row_num(what))
				temp=(tDownload*)(temp->prev);
			dq->insert_before(what,temp);
		};
		D4X_QVT->update(dq);
	};
};

void tMain::continue_download(tDownload *what) {
	if (CFG.OFFLINE_MODE) return;
	if (!what) return;
	switch (what->owner()) {
	case DL_SIZEQUERY:
		break;
	case DL_STOPWAIT:
		if (what->action!=ACTION_DELETE &&
		    what->action!=ACTION_REAL_DELETE)
			what->action=ACTION_CONTINUE;
		break;
	case DL_RUN:
		stop_download(what);
		if (what->owner()==DL_STOPWAIT){
			what->action=ACTION_CONTINUE;
			break;
		};
	default:
		MainLog->myprintf(LOG_OK,_("Continue downloading of file %s from %s..."),
				  what->info.file.c_str(),
				  what->info.host.c_str());
		d4xDownloadQueue *papa=what->myowner->PAPA;
		if (CFG.ALLOW_FORCE_RUN && what->owner()!=DL_PAUSE &&  try_to_run_download(what)) {
			papa->del(what);
			papa->add(what,DL_RUN);
		} else {
			papa->del(what);
			insert_into_wait_list(what,papa);
			try_to_run_wait(papa);
		};
	};
	what->Attempt.clear();
	what->finfo.size=-1;
};

void tMain::add_dir(tDownload *parent,int http) {
	if (parent==NULL || parent->DIR==NULL) return;
	tDownload *temp=parent->DIR->last();
	d4xDownloadQueue *q=D4X_QUEUE;
	D4X_QUEUE=parent->myowner->PAPA;
	while(temp) {
		parent->DIR->del(temp);
		int totop=parent->config->ftp_dirontop && temp->finfo.type==T_DIR;
		tDownload *ex=add_downloading(temp,totop);
		if (ex) {
			if (ex->config && ex->config->http_recurse_depth<temp->config->http_recurse_depth &&
			    ex->myowner==parent->myowner){
				ex->config->http_recurse_depth=temp->config->http_recurse_depth;
				continue_download(ex);
			};
			delete temp;
		};
		temp=parent->DIR->last();
	};
	D4X_QUEUE=q;
};

void tMain::speed_calculation(tDownload *what){
	DBC_RETURN_IF_FAIL(what!=NULL);
	time_t NOWTMP;
	time(&NOWTMP);
	switch(what->finfo.type) {
	case T_FILE:{
		time_t newdiff=NOWTMP-what->Start;
		time_t difdif=what->Difference-newdiff;
		/* detecting clock skew */
		if (difdif<-1800)
			what->Start+=difdif;
		else if (difdif>1800)
			what->Start-=difdif;
		what->Difference=NOWTMP-what->Start;
		int REAL_SIZE=what->finfo.size;
		if (REAL_SIZE==0 && what->who!=NULL)
			what->finfo.size=REAL_SIZE=what->who->another_way_get_size();
		if (what->who) what->Size=what->get_loaded();
		what->Remain=REAL_SIZE-what->Size;
		if (what->Difference!=0 && what->who) {
			what->Speed=what->SpeedCalc.speed();
		};
	};
	};
};

void tMain::print_info(tDownload *what) {
	DBC_RETURN_IF_FAIL(what!=NULL);
	if (CFG.WITHOUT_FACE){
		speed_calculation(what);
		return;
	};
	d4xDownloadQueue *PAPA=what->myowner->PAPA;
	char data[MAX_LEN];
	int downloading_started=0;
	if (what->who) {
		what->ActStatus=what->who->get_status();
		if (what->ActStatus==D_DOWNLOAD ||
		    what->status==DOWNLOAD_COMPLETE ||
		    what->status==DOWNLOAD_FATAL){
			downloading_started=1;
			if (!what->who->reget())
				what->ActStatus=D_DOWNLOAD_BAD;
		};
		if (!what->ActStatus) {
			what->ActStatus.reset();
//			DQV(what).set_run_icon(what);
		};
	};
	switch(what->finfo.type) {
	case T_FILE:{
		if (what->finfo.type!=what->finfo.oldtype)
			DQV(what).change_data(what->list_iter,FILE_TYPE_COL,_("file"));
		fsize_t REAL_SIZE=what->filesize();
		DQV(what).change_data(what->list_iter,FULL_SIZE_COL,make_number_nice(REAL_SIZE,PAPA->NICE_DEC_DIGITALS));
		what->Size=what->get_loaded();
		what->Remain=REAL_SIZE-what->Size;
		if (!what->Remain && what->Remain>=0)
			DQV(what).change_data(what->list_iter,REMAIN_SIZE_COL,
					      make_number_nice(fsize_t(what->Remain),PAPA->NICE_DEC_DIGITALS));
		time_t NOWTMP;
		time(&NOWTMP);
		if (what->Start>0) {
			time_t newdiff=NOWTMP-what->Start;
			time_t difdif=what->Difference-newdiff;
			/* detecting clock skew */
			if (difdif<-1800)
				what->Start+=difdif;
			else if (difdif>1800)
				what->Start-=difdif;
			what->Difference=NOWTMP-what->Start;
			DQV(what).change_data(what->list_iter,TIME_COL,convert_time(newdiff,PAPA->TIME_FORMAT));
		};
		if (!what->Size) {
			DQV(what).change_data(what->list_iter,DOWNLOADED_SIZE_COL,
					      make_number_nice(fsize_t(what->Size),PAPA->NICE_DEC_DIGITALS));
			time_t Pause=NOWTMP;
			if (Pause - what->Pause > 4)
				DQV(what).change_data(what->list_iter,PAUSE_COL);
			what->Pause = Pause;
		} else {
			if (what->status==DOWNLOAD_GO) {
				int Pause=NOWTMP-what->Pause;
				if (Pause>=30) 
					DQV(what).change_data(what->list_iter,PAUSE_COL,convert_time(Pause,PAPA->TIME_FORMAT));
			};
		};
		what->Percent=100;
		if (REAL_SIZE!=0)
			what->Percent=float((fsize_t(what->Size)*double(100))/double(REAL_SIZE));
/* setting new title of log*/
		if (CFG.USE_MAINWIN_TITLE){
			char title[MAX_LEN];
			std::string rfile=hexed_string(what->info.file);
			sprintf(title,"%2.0f%% %lli/%lli %s",what->Percent,fsize_t(what->Size),REAL_SIZE,rfile.c_str());
			log_window_set_title(what,title);
			log_window_set_split_info(what);
		};
		DQV(what).set_run_icon(what);

		if (!what->Size) {
			DQV(what).set_percent(what->list_iter,what->Percent);
		};
		what->Size.reset();
		/*	Speed calculation
		 */
		if (what->Difference!=0 && what->who) {
			what->Speed=what->SpeedCalc.speed();
			if (!what->Speed){
				DQV(what).change_data(what->list_iter,SPEED_COL,
						      make_number_nice(fsize_t(what->Speed),PAPA->SPEED_FORMAT?2:0));
				what->Speed.reset();
			};
			if (what->finfo.size>0 && what->Speed>0){
				fsize_t tmp=(REAL_SIZE-what->Size)/what->Speed;
				if (tmp>=0 && tmp<24*3660) {
					DQV(what).change_data(what->list_iter,ELAPSED_TIME_COL,
							      convert_time(tmp,PAPA->TIME_FORMAT));
				} else
					DQV(what).change_data(what->list_iter,ELAPSED_TIME_COL,"...");
			} else
				DQV(what).change_data(what->list_iter,ELAPSED_TIME_COL,"...");
		};
		break;
	};
	case T_DIR:{
		if (what->finfo.type!=what->finfo.oldtype)
			DQV(what).change_data(what->list_iter,FILE_TYPE_COL,_("dir"));
		break;
	};
	case T_LINK:{
		if (what->finfo.type!=what->finfo.oldtype)
			DQV(what).change_data(what->list_iter,FILE_TYPE_COL,_("link"));
		break;
	};
	case T_DEVICE:{
		if (what->finfo.type!=what->finfo.oldtype)
			DQV(what).change_data(what->list_iter,FILE_TYPE_COL,_("device"));
		break;
	};
	default:
		if (what->finfo.type!=what->finfo.oldtype)
			DQV(what).change_data(what->list_iter,FILE_TYPE_COL,"???");
	};
	if (what->finfo.type==T_DIR || what->finfo.type==T_NONE){
		if (what->who) what->Size=what->who->get_readed();
		if (!what->Size) {
			DQV(what).change_data(what->list_iter,DOWNLOADED_SIZE_COL,
					      make_number_nice(fsize_t(what->Size),PAPA->NICE_DEC_DIGITALS));
			what->Size.reset();
		};				
	};
	if (!what->Attempt) {
		what->Attempt.reset();
		if (what->config->number_of_attempts > 0)
			DQV(what).change_data(what->list_iter,TREAT_COL,
					      boost::lexical_cast<std::string>(fsize_t(what->Attempt))+"/"+
					      boost::lexical_cast<std::string>(what->config->number_of_attempts));
		else
			DQV(what).change_data(what->list_iter,TREAT_COL,boost::lexical_cast<std::string>(fsize_t(what->Attempt)));
	};
	what->finfo.oldtype=what->finfo.type;
};


void tMain::redirect(tDownload *what,d4xDownloadQueue *dq) {
	DBC_RETURN_IF_FAIL(what!=NULL);
	what->config->redirect_count+=1;
	if (what->config->redirect_count>10){
		what->delete_who();
		MainLog->myprintf(LOG_ERROR,_("Too many redirections!"),what);
		dq->add(what,DL_COMPLETE);
		what->finfo.type=T_NONE;
		return;
	};
	d4x::URL addr=what->redirect_url();
	if (addr.is_valid()) {
		/*
		if (what->config->leave_server==0 && equal_uncase(addr->host.get(),what->info->host.get())==0){
			MainLog->myprintf(LOG_ERROR,_("Redirection from [%z] to the different host forbidden by download's preferences!"),what);
			D4X_QUEUE->add(what,DL_COMPLETE);
			delete(addr);
			return;
		};
		*/
		if (addr==what->info && equal(what->config->referer.get(),std::string(what->info).c_str())){
			MainLog->myprintf(LOG_ERROR,_("Redirection from [%z] to the same location!"),what);
			dq->add(what,DL_COMPLETE);
			return;
		};
		if (ALL_DOWNLOADS->find(addr)) {
			dq->add(what,DL_COMPLETE);
			return;
		};
		ALL_DOWNLOADS->del(what);
		what->config->referer.set(std::string(what->info).c_str());
		if (addr.file==what->info.file)
			what->restart_from_begin=1;
		what->info=addr;
		ALL_DOWNLOADS->insert(what);
		tDownload *temp=dq->first(DL_WAIT);
		if (temp)
			dq->insert_before(what,temp);
		else
			dq->add(what,DL_WAIT);
//		normalize_path(what->get_SavePath());
		what->finfo.type=what->status=0;
		what->finfo.size=-1;
		if (CFG.WITHOUT_FACE==0){
			dq->qv.change_data(what->list_iter,URL_COL,what->info);
			dq->qv.set_filename(what);
			for (int i=FILE_TYPE_COL;i<PERCENT_COL;i++)
				if (i!=PERCENT_COL)
					DQV(what).change_data(what->list_iter,i,"");
		};
	}else{
		MainLog->myprintf(LOG_ERROR,_("Redirection from [%z] to nowhere!"),what);
		dq->add(what,DL_COMPLETE);
		what->finfo.type=T_NONE;
	};
	what->delete_who();
};

void tMain::post_stopping(tDownload *what){
	/* dispose tSegmentator only for main thread */
	if (what->segments){
		delete(what->segments);
		what->segments=NULL;
	};
	if (what->split && what->split->cond){
		delete(what->split->cond);
		what->split->cond=NULL;
	};
	if (what->config->isdefault && what->editor==NULL){
		delete(what->config);
		what->config=NULL;
	};
	if (what->editor) what->editor->enable_ok_button();
	FaceForPasswords->stop_matched(what);
};

void tMain::prepare_for_stoping_pre(tDownload *what){
	MainLog->myprintf(LOG_OK|LOG_DETAILED,_("Prepare thread %i of [%z] for stoping"),what->split?what->split->thread_num:1,what);
	if (what->SpeedLimit) SpeedScheduler->del(what->SpeedLimit);
	delete (what->SpeedLimit);
	what->SpeedLimit=NULL;
	if (what->WL){
		delete(what->WL);
		what->WL=NULL;
	};
};

void tMain::prepare_for_stoping(tDownload *what) {
	DBC_RETURN_IF_FAIL(what!=NULL);
	prepare_for_stoping_pre(what);
	if (what->split){
		what->split->grandparent->split->stopcount+=1;
		FaceForPasswords->limit_dec(what->split->grandparent);
//		what->split->run=0;
	}else
		FaceForPasswords->limit_dec(what);
};

void tMain::case_download_completed(tDownload *what){
	DBC_RETURN_IF_FAIL(what!=NULL);
	d4xDownloadQueue *papa=what->myowner->PAPA;
	papa->del(what);
	if (what->finfo.type==T_REDIRECT) {
		MainLog->myprintf(LOG_OK,_("Redirect from %z"),what);
		redirect(what,papa);
		real_stop_thread(what);
		post_stopping(what);
	}else{
		papa->add(what,DL_COMPLETE);
		if (what->file_type()==T_DIR) {
			MainLog->myprintf(LOG_OK,_("Downloading of directory %z was completed"),what);
			if (what->config->ftp_recurse_depth!=1) add_dir(what);
		} else {
			fsize_t bytes = what->finfo.size==0 ? what->who->get_readed():what->finfo.size;
			MainLog->myprintf(LOG_OK,_("Downloading of file %z (%ll bytes) was completed at speed %ll bytes/sec"),what,bytes,fsize_t(what->Speed));
			if (what->config->http_recurse_depth!=1 && what->DIR)
				add_dir(what,1);
		};
		real_stop_thread(what);
		post_stopping(what);
		if (papa->AUTODEL_COMPLETED && what->protect==0) {
			MainLog->myprintf(LOG_WARNING|LOG_DETAILED,_("%z was deleted from queue of downloads as completed download"),what);
			absolute_delete_download(what);
		};
	};
};

void tMain::case_download_failed(tDownload *what){
	DBC_RETURN_IF_FAIL(what!=NULL);
	d4xDownloadQueue *papa=what->myowner->PAPA;
	papa->del(what);
	papa->add(what,DL_STOP);
	MainLog->myprintf(LOG_ERROR,_("Downloading of file %z was terminated by fatal error"),what);
	if (papa->AUTODEL_FAILED && what->protect==0) {
		MainLog->myprintf(LOG_WARNING|LOG_DETAILED,_("%z was deleted from queue of downloads as failed download"),what);
		absolute_delete_download(what);
	};
};

void tMain::main_circle_first(tDownload *dwn){
/* look for stopped threads */
	tDownload *grandpapa=dwn;
	prepare_for_stoping(dwn);
	real_stop_thread(dwn);
	if (dwn->split){
		grandpapa=dwn->split->grandparent;
		if (grandpapa->split->stopcount!=grandpapa->split->NumOfParts &&
		    grandpapa->split->prepared)
			return;
	};
	int status=grandpapa->status;
	post_stopping(grandpapa);
	d4xDownloadQueue *papa=grandpapa->myowner->PAPA;
	if (status==DOWNLOAD_REAL_STOP ||
	    status==DOWNLOAD_COMPLETE  ||
	    status==DOWNLOAD_FATAL) {
		papa->del(grandpapa);
		papa->add(grandpapa,DL_PAUSE);
		switch(grandpapa->action){
		case ACTION_REAL_DELETE:
			delete_download(grandpapa,1);
			break;
		case ACTION_DELETE:
			delete_download(grandpapa,0);
			break;
		case ACTION_CONTINUE:
			continue_download(grandpapa);
			grandpapa->action=ACTION_NONE;
			break;
		case ACTION_STOP:
			grandpapa->action=ACTION_NONE;
			break;
		case ACTION_FAILED:
			grandpapa->action=ACTION_NONE;
			papa->del(grandpapa);
			papa->add(grandpapa,DL_STOP);
			break;
		case ACTION_SIZEQUERY:
			move_to_sizequery(grandpapa);
			break;
		};
	};
};

static bool _alt_equal_host_(tDownload *dwn,d4x::Alt *alt){
	return alt->info.host==dwn->info.host;
};

int tMain::try_to_switch(tDownload *dwn){
	if (dwn->ALTS==0 || dwn->ALTS->LST.empty()) return(0);
	d4xAltList *ALTS=dwn->ALTS;
	std::list<d4x::Alt*>::iterator alt=std::find_if(ALTS->LST.begin(),
							ALTS->LST.end(),
							std::bind1st(std::ptr_fun(_alt_equal_host_),dwn));
	if (alt!=ALTS->LST.end()) alt++;
	if (alt==ALTS->LST.end()){
		// first switching, add this url to its own alternates
		d4x::Alt *newalt=new d4x::Alt;
		newalt->info=dwn->info;
		dwn->ALTS->lock.lock();
		dwn->ALTS->add(newalt);
		dwn->ALTS->lock.unlock();
		alt=ALTS->LST.begin();
	};
	if (ALL_DOWNLOADS->find((*alt)->info)){
		return(0);
	};
	ALL_DOWNLOADS->del(dwn);
	dwn->info=(*alt)->info;
	ALL_DOWNLOADS->insert(dwn);
	FaceForPasswords->set_do_not_run(1);
	prepare_for_stoping(dwn);
	real_stop_thread(dwn);
	if (run_new_thread(dwn))
		return(0);
	FaceForPasswords->limit_inc(dwn);
	FaceForPasswords->set_do_not_run(0);
	if (dwn->split) dwn->split->stopcount-=1;
	return(1);
};

int tMain::try_to_switch_split(tDownload *dwn,tDownload *gp){
	if (gp==dwn) return(try_to_switch(dwn));
	if (gp->ALTS==NULL || gp->ALTS->LST.empty()) return(0);
	int n=dwn->split->alt+1;
	std::list<d4x::Alt*>::iterator alt=gp->ALTS->LST.begin();
	advance(alt,n);
	if (alt!=gp->ALTS->LST.end()){
		dwn->split->alt+=1;
		dwn->info=(*alt)->info;
	}else{
		dwn->info=gp->info;
	};
	dwn->split->run=0;
	FaceForPasswords->set_do_not_run(1);
	prepare_for_stoping(dwn);
	gp->split->stopcount-=1; //to avoid wrong stopcount
	real_stop_thread(dwn);
	try_to_run_split(gp);
	FaceForPasswords->set_do_not_run(0);
	return(1);
};

void tMain::check_split(tDownload *dwn){
	tDownload *grandparent=dwn->split->grandparent;
	if (dwn->status==DOWNLOAD_FATAL){
		if (try_to_switch_split(dwn,grandparent))
			return;
		stop_download(grandparent);
		grandparent->action=ACTION_FAILED;
		main_circle_first(dwn);
		try_to_run_wait(grandparent->myowner->PAPA);
		return;
	};
	if (grandparent->split->prepared){
		// we need to check overlaping event here for splited downloads
		// just find best part to download, and go! :-)
		if (dwn->WL->is_overlaped() || dwn->status==DOWNLOAD_COMPLETE){
			if (dwn->find_best_split() && dwn && dwn->who->reget()){
				real_stop_thread(dwn);
				prepare_for_stoping_pre(dwn);
				dwn->split->cond->inc();
				run_new_thread(dwn);
				return;
			};
		};
		prepare_for_stoping(dwn);
		try_to_run_split(grandparent);
		if (dwn!=grandparent) real_stop_thread(dwn);
		if (grandparent->split->NumOfParts==grandparent->split->stopcount)
			main_circle_second(grandparent);
	}else{
		try_to_run_split(grandparent);
		if (grandparent->status==DOWNLOAD_COMPLETE ||
		    grandparent->status==DOWNLOAD_FATAL)
			prepare_for_stoping(grandparent);
		main_circle_second(grandparent);
	};
};

void tMain::main_circle_second(tDownload *dwn){
/* look for completeted or faild threads */
	int failed=0,completed=0;
	d4xDownloadQueue *papa=dwn->myowner->PAPA;
	dwn->status_cp=dwn->status;
	switch(dwn->status) {
	case DOWNLOAD_COMPLETE:{
		if (dwn->segments)
			dwn->segments->complete();
		print_info(dwn); //to avoid wrong percentage after completing
		case_download_completed(dwn);
		completed=1;
		break;
	};
	case DOWNLOAD_FATAL:{
		if (dwn->split==NULL && try_to_switch(dwn)) return;
		case_download_failed(dwn);
		real_stop_thread(dwn);
		post_stopping(dwn);
		failed=1;
		break;
	};
	};
	int paparun=papa->count(DL_RUN);
	if (paparun==0){
		papa->speed.reset();
		if (!CFG.WITHOUT_FACE)
			D4X_QVT->update_speed(papa);
	};
	if (completed){
		if (paparun==0 &&
		    papa->count(DL_WAIT)==0)
			SOUND_SERVER->add_event(SND_QUEUE_FINISH);
		else
			SOUND_SERVER->add_event(SND_COMPLETE);
		try_to_run_wait(papa);
	};
	if (failed){
		try_to_run_wait(papa);
		SOUND_SERVER->add_event(SND_FAIL);
	};
};

void tMain::main_circle_nano1(){
	int i=10;
	D4X_UPDATE.lock();
	while(D4X_UPDATE.first && i>0){
		tDownload *dwn=D4X_UPDATE.first;
		if (dwn->owner()==DL_RUN){
			if (dwn->split)
				try_to_run_split(dwn);
			if (dwn->myowner->PAPA==D4X_QUEUE){
				print_info(dwn);
			};
			if (dwn->myowner && dwn->myowner->PAPA && !CFG.WITHOUT_FACE)
				D4X_QVT->update_speed(dwn->myowner->PAPA);
		};
		D4X_UPDATE.del();
		i--;
	};
	D4X_UPDATE.unlock();
};

void tMain::main_circle_nano2(){
	if (CFG.OFFLINE_MODE) return;
	D4X_UPDATE.lock_s();
	D4X_UPDATE.lock();
	int i=0;
	while (D4X_UPDATE.first_s){
		tDownload *dwn=D4X_UPDATE.first_s;
		tDownload *gp=dwn;
		D4X_UPDATE.del_s();
		D4X_UPDATE.del(dwn);
		if (dwn->split)
			gp=dwn->split->grandparent;
//		printf("%p %p\n",dwn,gp);
		switch(gp->owner()){
		case DL_RUN:
			if (dwn->split){
				check_split(dwn);
				break;
			}else{
				if (dwn->WL->is_overlaped() && dwn->status==DOWNLOAD_COMPLETE){
					real_stop_thread(dwn);
					prepare_for_stoping_pre(dwn);
					run_new_thread(dwn);
					break;
				};
				prepare_for_stoping(gp);
			};
			main_circle_second(gp);
			break;
		case DL_STOPWAIT:
			main_circle_first(dwn);
			break;
		case DL_SIZEQUERY:{
			real_stop_thread(dwn);
			prepare_for_stoping(dwn);
			d4xDownloadQueue *papa=dwn->myowner->PAPA;
			print_info(dwn);
			papa->del(dwn);
			if (dwn->action==DL_WAIT || dwn->action==DL_SIZEQUERY){
				dwn->sizequery=0;
				insert_into_wait_list(dwn,papa);
			}else{
				papa->add(dwn,dwn->action);
			};
			sizequery_run_first(papa);
			if (dwn->editor) dwn->editor->enable_ok_button();
			break;
		};
		default:
			break;
		};
		if (i++>5) break;
	};
	D4X_UPDATE.unlock();
	D4X_UPDATE.unlock_s();
};

int tMain::set_auto_run(int a){
	int old=DONTRY2RUN;
	DONTRY2RUN=a;
	return(old);
};

void tMain::try_to_run_run(d4xDownloadQueue *papa){
	tDownload *temp=papa->first(DL_RUN);
	while(temp) {
		tDownload *temp_next=(tDownload *)(temp->prev);
		int rvalue=try_to_run_download(temp);
		if (rvalue<0){
			papa->del(temp);
			papa->add(temp,DL_WAIT);
		}else{
			FaceForPasswords->match_and_check(temp,1);
			FaceForPasswords->limit_to_run(temp);
		};
		temp=temp_next;
	};
};

void tMain::try_to_run_wait(d4xDownloadQueue *papa){
	if (DONTRY2RUN) return;
	tDownload *temp=papa->first(DL_WAIT);
	while(temp && papa->count(DL_RUN)<papa->MAX_ACTIVE) {
		tDownload *temp_next=(tDownload *)(temp->prev);
		if (FaceForPasswords->match_and_check(temp)==0){
			FaceForPasswords->limit_to_run(temp);
			int rvalue=try_to_run_download(temp);
			if (rvalue<0){
				break;
			};
			if (rvalue) {
				papa->del(temp);
				papa->add(temp,DL_RUN);
			};
		};
		temp=temp_next;
	};
};

void tMain::main_circle() {
	if (ftpsearch) ftpsearch->cycle();
	speed();
	MainScheduler->run(this);
	if (CFG.WITHOUT_FACE==0){
		prepare_buttons();
		D4X_UPDATE.update(D4X_QUEUE->get_queue(DL_RUN));
	};
	GVARS.SOCKETS->kill_old();
};

void tMain::set_speed(int speed){
	CFG.SPEED_LIMIT=speed;
	if (CFG.SPEED_LIMIT>3) CFG.SPEED_LIMIT=3;
	if (CFG.SPEED_LIMIT<1) CFG.SPEED_LIMIT=1;
	if (CFG.WITHOUT_FACE==0) set_speed_buttons();
};

void tMain::check_for_remote_commands(){
	int i=0;
	while (!server->empty()){
		d4x::RemoteCommand addnew=server->get_command();
		switch (addnew.type){
		case PACKET_RERUN_FAILED:{
			MainLog->myprintf(LOG_FROM_SERVER|LOG_DETAILED,"%s %s",_("Restarting failed downloads"),_("[control socket]"));
			rerun_failed();
			break;
		};
		case PACKET_OPENLIST:{
			create_addlinks_with_referer(addnew.params,CFG.LOCAL_SAVE_PATH);
			break;
		};
		case PACKET_ADD_OPEN:{
			if (CFG.WITHOUT_FACE==0){
				if (addnew.params.size()>1)
					init_add_dnd_window(addnew.params[0].c_str(),NULL,addnew.params[1].c_str());
				else
					init_add_dnd_window(addnew.params[0].c_str(),NULL);
			};
			break;
		};
		case PACKET_STOP:{
//			MainLog->myprintf(LOG_FROM_SERVER,_("Stop the download via control socket [%s]"),addnew.params[0].c_str());
			stop_download_url(d4x::URL(addnew.params[0]));
			break;
		};
		case PACKET_DEL:{
			MainLog->myprintf(LOG_FROM_SERVER,_("Remove the download via control socket [%s]"),addnew.params[0].c_str());
			delete_download_url(d4x::URL(addnew.params[0]));
			break;
		};
		case PACKET_ADD:{
			TO_WAIT_IF_HERE=1;
			MainLog->myprintf(LOG_FROM_SERVER,_("Adding downloading via control socket [%s]"),addnew.params[0].c_str());
			if (addnew.params.size()>1)
				add_downloading(addnew.params[0].c_str(),CFG.LOCAL_SAVE_PATH,0,0,addnew.params[1].c_str());
			else
				add_downloading(addnew.params[0].c_str(),CFG.LOCAL_SAVE_PATH);
			TO_WAIT_IF_HERE=0;
			break;
		};
		case PACKET_SET_SPEED_LIMIT:{
			sscanf(addnew.params[0].c_str(),"%i",&CFG.SPEED_LIMIT);
			set_speed(CFG.SPEED_LIMIT);
			MainLog->myprintf(LOG_FROM_SERVER|LOG_DETAILED,_("Set speed limitation to %s %s"),
					  _(SPEED_LIMITATIONS_NAMES[CFG.SPEED_LIMIT]),
					  _("[control socket]"));
			break;
		};
		case PACKET_SET_SAVE_PATH:{
			/* to avoid misunderstandings we allow only absolute
			   pathes here 
			 */
			if (addnew.params[0].c_str() && addnew.params[0].c_str()[0]=='/'){
				delete[] CFG.LOCAL_SAVE_PATH;
				CFG.LOCAL_SAVE_PATH=copy_string(addnew.params[0].c_str());
			};
			break;
		};
		case PACKET_SET_MAX_THREADS:{
			sscanf(addnew.params[0].c_str(),"%i",&(D4X_QUEUE->MAX_ACTIVE));
			if (D4X_QUEUE->MAX_ACTIVE>50) D4X_QUEUE->MAX_ACTIVE=50;
			if (D4X_QUEUE->MAX_ACTIVE<0) D4X_QUEUE->MAX_ACTIVE=0;
			MainLog->myprintf(LOG_FROM_SERVER|LOG_DETAILED,"%s %i %s",_("Setup maximum active downloads to"),D4X_QUEUE->MAX_ACTIVE,_("[control socket]"));
			if (CFG.WITHOUT_FACE==0) D4X_QVT->update(D4X_QUEUE);
			break;
		};
		case PACKET_DEL_COMPLETED:{
			MainLog->myprintf(LOG_FROM_SERVER|LOG_DETAILED,"%s %s",_("Delete completed downloads"),_("[control socket]"));
			del_all_from_list(DL_COMPLETE);
			break;
		};
		case PACKET_MSG:
			MainLog->myprintf(LOG_FROM_SERVER,"%s %s",addnew.params[0].c_str(),_("[control socket]"));
			break;
		case PACKET_ICONIFY:
			if (CFG.WITHOUT_FACE==0) main_window_iconify();
			break;
		case PACKET_POPUP:
			if (CFG.WITHOUT_FACE==0) main_window_popup();
			break;
		case PACKET_EXIT_TIME:{
			int tmp;
			if (addnew.params[0].c_str() && sscanf(addnew.params[0].c_str(),"%d",&tmp)){
				if (tmp==0){
					CFG.EXIT_COMPLETE=0;
					MainLog->myprintf(LOG_FROM_SERVER,_("Exiting if nothing to do is switched off"),_("[control socket]"));
				};
				if (tmp>0){
					CFG.EXIT_COMPLETE=1;
					CFG.EXIT_COMPLETE_TIME=tmp;
					MainLog->myprintf(LOG_FROM_SERVER,_("Downloader will exit if nothing to do after %i minutes %s"),tmp,_("[control socket]"));
				};
			};
			break;
		};
		case PACKET_SWITCH_QUEUE:{
			int num=0;
			if (addnew.params[0].c_str() && sscanf(addnew.params[0].c_str(),"%d",&num)==1 && num>0){
				d4xDownloadQueue *q=d4x_get_queue_num(num);
				if (q){
					if (CFG.WITHOUT_FACE==0){
						D4X_QVT->switch_remote(q);
					}else{
						MainLog->myprintf(LOG_FROM_SERVER,_("Default queue is '%s' now."),q->name.get());
						D4X_QUEUE=q;
					};
				};
			};
			break;
		};
		};
		i+=1;
		if (i>10) break;
	};
};
//**********************************************/
void tMain::ftp_search(tDownload *what,int type){
	DBC_RETURN_IF_FAIL(what!=NULL);
	if (!what->info.file.empty()){
		tDownload *tmp=new tDownload;
		tmp->config=new tCfg;
		tmp->fsearch=type;
		tmp->set_default_cfg();
		tmp->info=what->info;
		tmp->finfo.size=what->finfo.size;
		tmp->info.proto=D_PROTO_SEARCH;
		if (tmp->split){
			delete(tmp->split);
			tmp->split=NULL;
		};
		ftpsearch->add(tmp);
	};
};

void tMain::ftp_search_name(char *name){
	if (name){
		tDownload *tmp=new tDownload;
		tmp->config=new tCfg;
		tmp->fsearch=0;
		tmp->set_default_cfg();
		tmp->info.file=name;
		tmp->finfo.size=-1;
		tmp->info.proto=D_PROTO_SEARCH;
		if (tmp->split){
			delete(tmp->split);
			tmp->split=NULL;
		};
		ftpsearch->add(tmp);
	};
};

void tMain::ftp_search_reping(tDownload *what){
	if (ftpsearch)
		ftpsearch->reping(what);
};

void tMain::ftp_search_remove(tDownload *what){
	if (ftpsearch)
		ftpsearch->remove(what);
};

void tMain::schedule_download(tDownload *what){
	if (what->owner()==DL_RUN || what->owner()==DL_STOPWAIT)
		return;
	DQV(what).remove(what);
	ALL_DOWNLOADS->del(what);
	what->myowner->PAPA->del(what);
	if (what->LOG){
		delete(what->LOG);
		what->LOG=NULL;
	};
	if (what->split){
		delete(what->split);
		what->split=NULL;
	};
	MainScheduler->add_scheduled(what);
};

void tMain::sizequery_run_first(d4xDownloadQueue *q){
	tDownload *torun=q->first(DL_SIZEQUERY);
	if (torun){
		if (torun->config==NULL){
			torun->config=new tCfg;
			torun->set_default_cfg();
		};
		if (torun->split)
			torun->split->grandparent=torun;
		run_new_thread(torun);
	};
};

void tMain::move_to_sizequery(tDownload *what){
	if (CFG.OFFLINE_MODE) return;
	if (what==NULL) return;
	if (what->owner()==DL_STOPWAIT){
		what->action=ACTION_SIZEQUERY;
		return;
	};
	int owner=what->owner();
	if (owner!=DL_SIZEQUERY && owner!=DL_RUN){
		d4xDownloadQueue *papa=what->myowner->PAPA;
		what->action=owner;
		papa->del(what);
		papa->add(what,DL_SIZEQUERY);
		what->sizequery=1;
		if (papa->count(DL_SIZEQUERY)==1)
			sizequery_run_first(papa);
	};
};

tDownload *tMain::add_downloading(tDownload *what,int to_top) {
	tDownload *tdwn=NULL;
	if ((tdwn=ALL_DOWNLOADS->find(what)) || !what->info.is_valid()) {
		printf("%s\n",std::string(what->info).c_str());
		if (TO_WAIT_IF_HERE && tdwn && tdwn->owner()!=DL_RUN){
			continue_download(tdwn);
		};
		return(tdwn);
	};
	if (what->ScheduleTime){
		MainScheduler->add_scheduled(what);
		return(NULL);
	};
	if (what->config==NULL){
		FaceForPasswords->set_cfg(what);
	};
	ALL_DOWNLOADS->insert(what);
	tDownload *f=D4X_QUEUE->first(DL_WAIT);
	if (to_top && f)
		D4X_QUEUE->insert_before(what,f);
	else		
		D4X_QUEUE->add(what);
	if (to_top)
		D4X_QUEUE->qv.add_first(what);
	else
		D4X_QUEUE->qv.add(what);
	try_to_run_wait(D4X_QUEUE);
	return(NULL);
};

tDownload *tMain::add_downloading_to(tDownload *what,int to_top) {
	DBC_RETVAL_IF_FAIL(what!=NULL,NULL);	
	int owner=what->status;
	if (what->ScheduleTime){
		MainScheduler->add_scheduled(what);
		return NULL;
	};
	if (owner>DL_ALONE && owner<DL_TEMP){
		DONTRY2RUN=1;
		if (add_downloading(what,to_top)){
			tDownload *dwn=ALL_DOWNLOADS->find(what);
//			if (dwn && CFG.WITHOUT_FACE==0 && CFG.NEED_DIALOG_FOR_DND==0){
//				D4X_QVT->move_to(dwn);
//			};
			delete (what);
			DONTRY2RUN=0;;
			return dwn;
		};
		switch(owner){
		case DL_RUN:{
			if (try_to_run_download(what)){
				D4X_QUEUE->del(what);
				D4X_QUEUE->add(what,DL_RUN);
			};
			break;
		};
		case DL_STOPWAIT:
		case DL_STOP:{
			D4X_QUEUE->del(what);
			D4X_QUEUE->add(what,DL_STOP);
			break;
		};
		case DL_COMPLETE:{
			D4X_QUEUE->del(what);
			D4X_QUEUE->add(what,DL_COMPLETE);
			break;
		};
		case DL_PAUSE:{
			D4X_QUEUE->del(what);
			D4X_QUEUE->add(what,DL_PAUSE);
			break;
		};
		};
		DONTRY2RUN=0;
		try_to_run_wait(D4X_QUEUE);
	}else{
		delete(what);
	};
	return(NULL);
};

int tMain::add_downloading(const char *adr,char *where,char *name,char *desc,const char *referer) {
	if (adr==NULL) return -1;
//	if (!addr->is_valid()) return -1;
	tDownload *whatadd=new tDownload;
	whatadd->info=std::string(adr);
	if (where && *where) {
		whatadd->config=new tCfg;
		whatadd->set_default_cfg();
		whatadd->config->save_path.set(where);
		whatadd->config->isdefault=0;
	};
	if (referer && *referer){
		if (whatadd->config==0){
			whatadd->config=new tCfg;
			whatadd->set_default_cfg();
			whatadd->config->isdefault=0;
		};
		whatadd->config->referer.set(referer);
	};
	if (whatadd->info.file.empty()) {
		whatadd->finfo.type=T_DIR;
		whatadd->finfo.size=0;
	};
//	normalize_path(whatadd->get_SavePath());

	if (name && strlen(name) && whatadd->config){
		whatadd->Name2Save=name;
	};

	whatadd->Description.set(desc);

	if (add_downloading(whatadd)) {
		delete(whatadd);
		return -1;
	};
	return 0;
};

unsigned int tMain::get_precise_time(){
#if (defined(BSD) && (BSD >= 199306))
	struct timeval tp;
	gettimeofday(&tp, NULL);
	return(tp.tv_sec*1000+tp.tv_usec/1000);
#else
	struct timeb tp;
	ftime(&tp);
	return(tp.time*1000+tp.millitm);
#endif
};

void tMain::speed() {
	unsigned int curent_time=get_precise_time();
	unsigned int TimeLeft=curent_time-LastTime;
	tMeter::BSize readed_bytes=GVARS.READED_BYTES;
	tMeter::BSize bytes=readed_bytes-LastReadedBytes;
	if (TimeLeft!=0){
		int Speed=((bytes*1000)/TimeLeft);
		LastReadedBytes=readed_bytes;
		GlobalMeter->add(Speed);
		GraphMeter->add(Speed);
		LastTime=curent_time;
	};
	int SPEED_LIMIT=0;
	switch (CFG.SPEED_LIMIT) {
		case 1:	{
				SPEED_LIMIT=(TimeLeft*CFG.SPEED_LIMIT_1)/1000;
				break;
			};
		case 2:	{
				SPEED_LIMIT=(TimeLeft*CFG.SPEED_LIMIT_2)/1000;
				break;
			};
		case 3:
		default:{
				SPEED_LIMIT=0;
		};
	};
	if (SPEED_LIMIT){
//		SPEED_LIMIT+=(prev_speed_limit-bytes)/2;
		if (SPEED_LIMIT>0)
			SpeedScheduler->schedule(SPEED_LIMIT,1);
	}else
		SpeedScheduler->schedule(TimeLeft);
	prev_speed_limit=SPEED_LIMIT;
};

void tMain::run(int argv,char **argc) {
	SOUND_SERVER->run_thread();
	if (CFG.WITHOUT_FACE==0){
		if (CFG.USE_THEME && CFG.THEME_FILE){
			char *tmp=sum_strings(CFG.THEMES_DIR,"/",CFG.THEME_FILE,".xml",NULL);
			D4X_THEME_DATA=d4x_xml_parse_file(tmp);
			delete[] tmp;
		};
		ftpsearch=new tFtpSearchCtrl;
		init_face(argv,argc);
		ftpsearch->init(FSearchView,this,MainLog);
		fs_list_set_size();
	};
	init_main_log();
	MainLog->add(VERSION_NAME,LOG_WARNING);
			  
	COOKIES=new tCookiesTree;
	COOKIES->load_cookies();
	load_defaults();
	if (CFG.WITHOUT_FACE==0){
		prepare_buttons();
		init_timeouts();
	};
	parse_command_line_postload(argv,argc);
	server->run_thread();
	LastTime=get_precise_time();
	var_check_all_limits();
	MainLog->add(_("Loading FTP-Search engines"),LOG_WARNING);
	D4X_SEARCH_ENGINES.load();
	MainLog->add(_("Normally started"),LOG_WARNING);
	check_for_remote_commands();
	GlobalMeter->set_mode(CFG.GRAPH_MODE);
	GraphMeter->set_mode(CFG.GRAPH_MODE);
	MainScheduler->clear_old();
	if (CFG.WITHOUT_FACE==0){
		SOUND_SERVER->add_event(SND_STARTUP);
		gtk_main();
	}else{
		run_without_face();
	};
};

void tMain::run_without_face(){
	int TIME_FOR_SAVING=CFG.SAVE_LIST_INTERVAL * 60;
	int COMPLETE_INTERVAL=CFG.EXIT_COMPLETE_TIME * 60;
	struct timespec ival={0,200000000};
	int i=0;
	while(1){
		check_for_remote_commands();
		main_circle_nano2();
		if (i++>=5){
			main_circle();
			i=0;
		};
		nanosleep(&ival,NULL);
//		sleep(1);
		TIME_FOR_SAVING-=1;
		if (!TIME_FOR_SAVING) {
			if (CFG.SAVE_LIST) {
				save_list();
			};
			TIME_FOR_SAVING=CFG.SAVE_LIST_INTERVAL * 60;
		};
		if (CFG.EXIT_COMPLETE && d4x_run_or_wait_downloads()==0){
			COMPLETE_INTERVAL-=1;
			if (COMPLETE_INTERVAL<0){
				break;
			};
		}else{
			COMPLETE_INTERVAL=CFG.EXIT_COMPLETE_TIME * 60;
		};
	};
	save_list();
	done();
	save_config();
	for (int i=0;i<LAST_HISTORY;i++)
		delete(ALL_HISTORIES[i]);
};

void tMain::run_after_quit(){
	if (CFG.EXEC_WHEN_QUIT && strlen(CFG.EXEC_WHEN_QUIT))
		system(CFG.EXEC_WHEN_QUIT);
};

void tMain::add_download_message(tDownload *what) {
	if (!what) return;
	std::string rfile=hexed_string(what->info.file);
	MainLog->myprintf(LOG_OK,_("Added downloading of file %s from %s [by user]"),rfile.c_str(),what->info.host.c_str());
};

static int not_all_stopped(tQueue *q){
	d4xDownloadQueue *dq=(d4xDownloadQueue *)q->first();
	while(dq){
		if (dq->count(DL_STOPWAIT))
			return(1);
		if (not_all_stopped(&(dq->child)))
			return(1);
		dq=(d4xDownloadQueue *)(dq->prev);
	};
	return(0);
};

void tMain::stop_all(tQueue *q){
	d4xDownloadQueue *dq=(d4xDownloadQueue *)q->first();
	while(dq){
		stop_all(&(dq->child));
		tDownload *d=dq->first(DL_RUN);
		while (d){
			stop_download(d);
			d=dq->first(DL_RUN);
		}
		dq=(d4xDownloadQueue *)(dq->prev);
	};
};

void tMain::stop_all_offline(tQueue *q){
	d4xDownloadQueue *dq=(d4xDownloadQueue *)q->first();
	while(dq){
		stop_all_offline(&(dq->child));
		tDownload *d=dq->first(DL_RUN);
		while (d){
			stop_download(d);
			d->action=ACTION_CONTINUE;
			d=dq->first(DL_RUN);
		}
		d=dq->first(DL_SIZEQUERY);
		if (d)
			stop_download(d);
		dq=(d4xDownloadQueue *)(dq->prev);
	};
};

void tMain::switch_offline_mode(){
	if (CFG.OFFLINE_MODE==0){
		CFG.OFFLINE_MODE=1;
		stop_all_offline(&D4X_QTREE);
		ftpsearch->stop_all_offline();
		MainLog->add(_("Downloader is in offline mode now"),LOG_WARNING|LOG_DETAILED);
	}else{
		CFG.OFFLINE_MODE=0;
		int tmpfr=CFG.ALLOW_FORCE_RUN;
		CFG.ALLOW_FORCE_RUN=1;
		main_circle_nano2();
		CFG.ALLOW_FORCE_RUN=tmpfr;
		MainLog->add(_("Offline mode is turned off"),LOG_WARNING|LOG_DETAILED);
	};
	if (!CFG.WITHOUT_FACE){
		d4x_main_offline_mode();
	};
};

void tMain::done() {
	/* There are  we MUST stop all threads!!!
	 */
	server->stop_thread();

	/* removing ftpsearch before removing all queues
	   to avoid segfault at host-limit checks */
	if (ftpsearch) delete(ftpsearch);
	FaceForPasswords->save();
	if (FaceForPasswords)
		delete (FaceForPasswords);
	stop_all(&D4X_QTREE);
	while(not_all_stopped(&D4X_QTREE)){
		main_circle_nano2();
		sleep(1);
	};
	D4X_QUEUE->done();
	MainScheduler->save();
	delete(MainScheduler);
	delete(GlobalMeter);
	delete(GraphMeter);
	delete(GraphLMeter);
	delete(LocalMeter);
	delete(ALL_DOWNLOADS); // delete or not delete that is the question :-)
	COOKIES->save_cookies();
	delete(COOKIES);
	MainLog->init_list(NULL);
	MainLog->myprintf(LOG_OK,_("%lu bytes loaded during the session"),GVARS.READED_BYTES);
	MainLog->add(_("Downloader exited normaly"),LOG_OK);
	delete(MainLog);
	delete(SpeedScheduler);

	close(LOCK_FILE_D);
	delete (server);
	delete (MsgQueue);
	/*
	for (int i=URL_HISTORY;i<LAST_HISTORY;i++)
		if (ALL_HISTORIES[i]) delete(ALL_HISTORIES[i]);
	*/
	SOUND_SERVER->stop_thread();
	delete(SOUND_SERVER);
	delete(GVARS.SOCKETS);
	if (D4X_THEME_DATA) delete(D4X_THEME_DATA);
	if (LOCK_FILE) remove(LOCK_FILE);
	if (D4X_QVT) delete(D4X_QVT);
};

void tMain::reinit_main_log() {
	MainLog->reinit(CFG.MAX_MAIN_LOG_LENGTH);
	MainLog->reinit_file();
};
//*****************************************************************/
//-----------------------------------------------------------------
void *download_last(void *nothing) {
	tDownload *what=(tDownload *)nothing;
	my_pthread_key_init();
	my_pthread_key_set(what);
	init_signal_handler();
	if (what) {
		d4x::URL *addr=&(what->info);
		what->LOG->MsgQueue=LogsMsgQueue;
		if (what->config->log_save_path.get()){
			char *real_path=parse_save_path(what->config->log_save_path.get(),what->info.file.c_str());
			make_dir_hier_without_last(real_path);
			if (what->LOG->init_save(real_path)){
				what->WL->log_printf(LOG_ERROR,
						     _("Can't open '%s' to save log"),
						     real_path);
			};
			delete[] real_path;
		}else{
			what->LOG->init_save(NULL);
		};
		if ((what->config->proxy.http_host.get() && addr->proto==D_PROTO_HTTP) ||
		     (what->config->proxy.ftp_host.get() && addr->proto==D_PROTO_FTP && what->config->proxy.type)) {
			what->who=new tProxyDownload(what->WL);
			what->download_http();
			pthread_exit(NULL);
			return NULL;
		};
		switch(addr->proto){
		case D_PROTO_SEARCH:
			if (what->who==NULL){
				if (what->config->proxy.http_host.get())
					what->who=new tProxyDownload(what->WL);
				else
					what->who=new tHttpDownload(what->WL);
			};
			what->ftp_search();
			break;
#ifdef HAVE_SSL
		case D_PROTO_HTTPS:
#endif //HAVE_SSL
		case D_PROTO_HTTP:
			what->download_http();
			break;
		case D_PROTO_FTP:
			what->download_ftp();
			break;
		default:
			what->WL->log(LOG_ERROR,_("Such protocol is not supported!"));
			what->download_failed();
			break;
		};
	};
	pthread_exit(NULL);
	return NULL;
};
//------------------------------------------------------------------------

