//---------------------------------------------------------
void CData_Source_PgSQL::Update_Sources(const wxTreeItemId &Root)
{
	Freeze();

	//-----------------------------------------------------
	wxTreeItemIdValue Cookie; wxTreeItemId Item = GetFirstChild(Root, Cookie);

	while( Item.IsOk() )
	{
		Update_Source(Item);

		Item	= GetNextChild(Item, Cookie);
	}

	//-----------------------------------------------------
	CSG_Table	Connections;

	RUN_MODULE(DB_PGSQL_Get_Connections, false, SET_PARAMETER("CONNECTIONS", &Connections));	// CGet_Connections

	for(int i=0; i<Connections.Get_Count(); i++)
	{
		if( !Find_Source(Connections[i].asString(0)) )
		{
			Update_Source(Connections[i].asString(0));
		}
	}

	//-----------------------------------------------------
	SortChildren(Root);
	Expand      (Root);

	Thaw();
}
//---------------------------------------------------------
void CData_Source_PgSQL::Source_Open(const wxTreeItemId &Item)
{
	CData_Source_PgSQL_Data	*pData	= Item.IsOk() ? (CData_Source_PgSQL_Data *)GetItemData(Item) : NULL; if( pData == NULL )	return;

	if( pData->Get_Type() == TYPE_ROOT
	||  pData->Get_Type() == TYPE_SERVER )
	{
		CSG_Module	*pModule	= SG_Get_Module_Library_Manager().Get_Module("db_pgsql", DB_PGSQL_Get_Connection);	// CGet_Connection

		if(	pModule && pModule->On_Before_Execution() )
		{
			if( pData->Get_Type() == TYPE_SERVER )
			{
				pModule->Set_Parameter("PG_HOST", pData->Get_Host());
				pModule->Set_Parameter("PG_PORT", pData->Get_Port());
			}

			if( DLG_Parameters(pModule->Get_Parameters()) )
			{
				pModule->Execute();
			}
		}
	}
	else if( pData->is_Connected() )
	{
		Update_Source(Item);
	}
	else if( !Source_Open(pData, true) )
	{
		DLG_Message_Show_Error(_TL("Could not connect to data source."), _TL("Connect to PostgreSQL"));
	}
}
//---------------------------------------------------------
void CData_Source_PgSQL::Update_Item(const wxTreeItemId &Item)
{
	CData_Source_PgSQL_Data	*pData	= Item.IsOk() ? (CData_Source_PgSQL_Data *)GetItemData(Item) : NULL; if( pData == NULL )	return;

	switch( pData->Get_Type() )
	{
	case TYPE_ROOT:		Update_Sources();		break;
	case TYPE_SERVER:	Update_Sources(Item);	break;
	case TYPE_SOURCE:	Update_Source (Item);	break;
	}
}
//---------------------------------------------------------
CData_Source_PgSQL::CData_Source_PgSQL(wxWindow *pParent)
	: wxTreeCtrl(pParent, ID_WND_DATA_SOURCE_DATABASE)
{
	AssignImageList(new wxImageList(IMG_SIZE_TREECTRL, IMG_SIZE_TREECTRL, true, 0));
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_DB_SOURCES    );	// IMG_ROOT
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_DB_SOURCES    );	// IMG_SERVER
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_DB_SOURCE_OFF );	// IMG_SRC_CLOSED
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_DB_SOURCE_ON  );	// IMG_SRC_OPENED
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_DB_TABLE      );	// IMG_TABLE
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_SHAPES_POINT  );	// IMG_POINT
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_SHAPES_POINTS );	// IMG_POINTS
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_SHAPES_LINE   );	// IMG_LINE
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_SHAPES_POLYGON);	// IMG_POLYGON
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_GRID_MANAGER  );	// IMG_GRIDS
	IMG_ADD_TO_TREECTRL(ID_IMG_WKSP_GRID          );	// IMG_GRID

	AddRoot(_TL("PostgreSQL Sources"), IMG_ROOT, IMG_ROOT, new CData_Source_PgSQL_Data(TYPE_ROOT));

	//-----------------------------------------------------
	SG_UI_Msg_Lock(true);

	wxString	Server;

	for(int i=0; CONFIG_Read(CFG_PGSQL_DIR, wxString::Format(CFG_PGSQL_SRC, i), Server); i++)
	{
		wxString	User, Password;

		if( Server.Find("|") > 0 )
		{
			User     = Server.AfterFirst ('|').BeforeFirst('|');
			Password = Server.AfterLast  ('|');
			Server   = Server.BeforeFirst('|');
		}

		CData_Source_PgSQL_Data	*pData	= new CData_Source_PgSQL_Data(TYPE_SOURCE, &Server, &Server, &User, &Password);

		Update_Source(AppendItem(Get_Server_Item(Server, true), pData->Get_DBName().c_str(), IMG_SRC_CLOSED, IMG_SRC_CLOSED, pData));
	}

	Update_Sources();

	SG_UI_Msg_Lock(false);
}
//---------------------------------------------------------
void CData_Source_PgSQL::Update_Source(const wxString &Server)
{
	if( Server.IsEmpty() )
	{
		Update_Sources();

		return;
	}

	wxTreeItemId	Item	= Find_Source(Server);

	if( !Item.IsOk() && PGSQL_is_Connected(&Server) )
	{
		CData_Source_PgSQL_Data	*pData	= new CData_Source_PgSQL_Data(TYPE_SOURCE, &Server, &Server);

		Item	= AppendItem(Get_Server_Item(Server, true), pData->Get_DBName().c_str(), IMG_SRC_OPENED, IMG_SRC_OPENED, pData);
	}

	Update_Source(Item);
}
int SB_Source_Add(const char* name, const char* address, sb_source_type_t type)
{
	source_data *s;
	char addr[512];
	int pos;

	if (strlen(name) <= 0  ||  strlen(address) <= 0)
		return -1;

	// create new source
	s = Create_Source();
	s->type = type;
	strlcpy (s->name, name, sizeof (s->name));
	strlcpy (addr, address, sizeof (addr));

	if (s->type == type_file) {
		strlcpy (s->address.filename, address, sizeof (s->address.filename));
	}
	else if (s->type == type_url) {
		strlcpy(s->address.url, address, sizeof(s->address.url));
	}
	else {
		if (!strchr(addr, ':')) {
			strlcat (addr, ":27000", sizeof (addr));
		}
		if (!NET_StringToAdr(addr, &(s->address.address))) {
			return -1;
		}
	}

	pos = sourcesn++;
	sources[pos] = s;
	Mark_Source(sources[pos]);
	Update_Source(sources[pos]);

	SB_Sources_Dump();

	return pos;
}
void Reload_Sources(void)
{
    int i;
	vfsfile_t *f;
	char ln[2048];
    source_data *s;

	SB_ServerList_Lock();
    for (i=0; i < sourcesn; i++)
        Delete_Source(sources[i]);
    sourcesn = 0;

    // create dummy unbound source
    sources[0] = Create_Source();
    sources[0]->type = type_dummy;
    strlcpy (sources[0]->name, "Unbound", sizeof (sources[0]->name));
    sources[0]->servers = (server_data **) Q_malloc(MAX_UNBOUND*sizeof(server_data *));
	sources[0]->serversn = 0;
	sources[0]->servers_allocated = MAX_UNBOUND;

	sourcesn = 1;

	f = FS_OpenVFS(SOURCES_LIST_FILENAME, "rb", FS_ANY);
	if (!f) 
	{
        //Com_Printf ("sources file not found: %s\n", SOURCES_PATH);
		SB_ServerList_Unlock();
		return;
	}

    s = Create_Source();
    while (VFS_GETS(f, ln, sizeof(ln)))
    {
		char line[2048];
        char *p, *q;

        if (sscanf(ln, "%[ -~	]s", line) != 1) {
			continue;
		}

        p = next_nonspace(line);
        if (*p == '/')
            continue;   // comment
        q = next_space(p);

		if (!strncmp(p, "master", q-p)) {
            s->type = type_master;
		}
		else if (!strncmp(p, "file", q-p)) {
			s->type = type_file;
		}
		else if (!strncmp(p, "url", q-p)) {
			s->type = type_url;
		}
		else {
			continue;
		}

        p = next_nonspace(q);
        q = (*p == '\"') ? next_quote(++p) : next_space(p);

        if (q-p <= 0)
            continue;

        strlcpy (s->name, p, min(q-p+1, MAX_SOURCE_NAME+1));

        p = next_nonspace(q+1);
        q = next_space(p);
        *q = 0;

        if (q-p <= 0)
            continue;

        if (s->type == type_file)
            strlcpy (s->address.filename, p, sizeof (s->address.filename));
		else if (s->type == type_url)
			strlcpy (s->address.url, p, sizeof (s->address.url));
        else
            if (!NET_StringToAdr(p, &(s->address.address)))
                continue;

        sources[sourcesn] = Create_Source();
        i = sources[sourcesn]->unique;
        memcpy(sources[sourcesn], s, sizeof(source_data));
        sources[sourcesn]->unique = i;
        sourcesn++;
    }

    Delete_Source(s);
	VFS_CLOSE(f);

    //Com_Printf("Read %d sources for Server Browser\n", sourcesn);

    // update all file sources
    for (i=0; i < sourcesn; i++)
        if (sources[i]->type == type_file)
            Update_Source(sources[i]);
        else if (sources[i]->type == type_master || sources[i]->type == type_url)
            Precache_Source(sources[i]);

    rebuild_servers_list = 1;
    resort_sources = 1;
	SB_ServerList_Unlock();
}
DWORD WINAPI Update_Multiple_Sources_Proc(void * lpParameter)
{
    // get servers from master server
    SYSTEMTIME lt;
    char request[] = {'c', '\n', '\0'};

    socket_t newsocket;
	struct sockaddr_storage server;
    int ret = 0, i, sourcenum;
    unsigned char answer[10000];
    fd_set fd;
    struct timeval tv;
    int total_masters = 0;
    int updated = 0;
    int d1, d2;

    GetLocalTime(&lt);
    d1 = lt.wSecond + 60*(lt.wMinute + 60*(lt.wHour + 24*(lt.wDay)));
    // update file sources - this should be a flash
    for (sourcenum = 0; sourcenum < psourcesn; sourcenum++)
        if (psources[sourcenum]->checked)
        {
            if (psources[sourcenum]->type == type_file)
                Update_Source(psources[sourcenum]);
			if (psources[sourcenum]->type == type_url)
				Update_Source(psources[sourcenum]); // todo cache this too
            else if (psources[sourcenum]->type == type_master)
            {
                source_data *s = psources[sourcenum];
                if (s->last_update.wYear != 0  &&  !source_full_update)
                {
                    d2 = s->last_update.wSecond + 60*(s->last_update.wMinute + 60*(s->last_update.wHour + 24*(s->last_update.wDay)));

                    if (d1 > d2  &&  d1 < d2 + sb_sourcevalidity.value*60)
                    continue;
                }
                total_masters++;
            }
        }
	
    // update master sources
    newsocket = UDP_OpenSocket(PORT_ANY);

    for (sourcenum = 0; sourcenum < psourcesn  &&  !abort_ping; sourcenum++)
    {
        server_data *servers[MAX_SERVERS];
        int serversn = 0;
        int trynum = 0;
        source_data *s = psources[sourcenum];
		double timeout;

        if (psources[sourcenum]->type != type_master  ||  !psources[sourcenum]->checked)
            continue;

        if (s->last_update.wYear != 0  &&  !source_full_update)
        {
            d2 = s->last_update.wSecond + 60*(s->last_update.wMinute + 60*(s->last_update.wHour + 24*(s->last_update.wDay)));

            if (d1 > d2  &&  d1 < d2 + sb_sourcevalidity.value*60)
                continue;
        }

		// send trynum queries to master server
        for (trynum=0; trynum < sb_masterretries.value; trynum++)
        {
			NetadrToSockadr (&(s->address.address), &server);
            ret = sendto (newsocket, request, sizeof(request), 0,
                          (struct sockaddr *)&server, sizeof(server) );
		}

		if (ret <= 0)
			continue;

		timeout = Sys_DoubleTime() + (sb_mastertimeout.value / 1000.0);
		while (Sys_DoubleTime() < timeout) {
			struct sockaddr_storage hostaddr;
            netadr_t from;

            //fd.fd_count = 1;
            //fd.fd_array[0] = newsocket;
			FD_ZERO(&fd);
			FD_SET(newsocket, &fd);
            tv.tv_sec = 0;
            tv.tv_usec = 1000 * sb_mastertimeout.value;
            ret = select(newsocket+1, &fd, NULL, NULL, &tv);

            // get answer
            i = sizeof(hostaddr);
            if (ret > 0)
                ret = recvfrom (newsocket, (char *) answer, 10000, 0,
				(struct sockaddr *)&hostaddr, (socklen_t *)&i);

            if (ret > 0  &&  ret < 10000)
            {
                SockadrToNetadr (&hostaddr, &from);

                if (from.ip[0] == s->address.address.ip[0] &&
                    from.ip[1] == s->address.address.ip[1] &&
                    from.ip[2] == s->address.address.ip[2] &&
                    from.ip[3] == s->address.address.ip[3] &&
                    from.port == s->address.address.port)
                {
                    answer[ret] = 0;

                    if (memcmp(answer, "\xff\xff\xff\xff\x64\x0a", 6))
                    {
                        continue;
                    }

                    // create servers avoiding duplicates
					for (i=6; i+5 < ret; i+=6)
					{
						char buf[32];
						server_data* server;
						qbool exists = false;
						int j;

						snprintf(buf, sizeof (buf), "%u.%u.%u.%u:%u",
							(int)answer[i+0], (int)answer[i+1],
							(int)answer[i+2], (int)answer[i+3],
							256 * (int)answer[i+4] + (int)answer[i+5]);

						server = Create_Server(buf);
						for (j=0; j<serversn; j++) {
							if (NET_CompareAdr(servers[j]->address, server->address)) {
								exists = true;
								break;
							}
						}
						
						if (!exists)
							servers[serversn++] = server;
						else
							Delete_Server(server);
					}
                }
            }
		}

        // copy all servers to source list
        if (serversn > 0)
        {
			updated++;

			SB_ServerList_Lock();

            Reset_Source(s);
            s->servers = (server_data **) Q_malloc(serversn * sizeof(server_data *));
            for (i=0; i < serversn; i++)
                s->servers[i] = servers[i];
            s->serversn = serversn;
			s->servers_allocated = serversn;
            if (s->checked)
                rebuild_servers_list = 1;
            GetLocalTime(&(s->last_update));

			SB_ServerList_Unlock();

            if (sb_mastercache.value)
                DumpSource(s);
        }

        ping_pos = updated / (double)total_masters;
    }

    closesocket(newsocket);

	// Not having this here leads to crash almost always when some
	// other action with servers list happens right after this function.
	// Even 1 ms delay was enough during the tests, previously 500 ms was used.
    //Sys_MSleep(100);

    updating_sources = 0;
	sb_queuedtriggers |= SB_TRIGGER_SOURCESUPDATED;
    return 0;
}