void DumpSource(source_data *s)
{
    FILE *f;
    int i;
    char buf[1024];

    if (s->type == type_file)
        snprintf(buf, sizeof (buf), "sb/%s", s->address.filename);
    else if (s->type == type_master)
    {
        Sys_mkdir("sb/cache");
        snprintf(buf, sizeof (buf), "sb/cache/%d_%d_%d_%d_[%d].txt",
                s->address.address.ip[0], s->address.address.ip[1],
                s->address.address.ip[2], s->address.address.ip[3],
                ntohs(s->address.address.port));
    }
    else
        return;

//    f = fopen(buf, "wt");
//    if (f == NULL)
//        return;
    if (!FS_FCreateFile(buf, &f, "ezquake", "wt"))
        return;

    for (i=0; i < s->serversn; i++)
        fprintf(f, "%d.%d.%d.%d:%d\n",
        s->servers[i]->address.ip[0], s->servers[i]->address.ip[1],
        s->servers[i]->address.ip[2], s->servers[i]->address.ip[3],
        ntohs(s->servers[i]->address.port));

    fclose(f);
}
void QTVList_Cache_File_Download(void)
{
	CURL *curl;
	CURLcode res;
	FILE *f;

	curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_URL, sb_qtvlist_url.string);
	}
	else {
		Com_Printf_State(PRINT_FAIL, "QTVList_List_f() Can't init cURL\n");
		return;
	}

	if (!FS_FCreateFile(QTVLIST_CACHE_FILE, &f, QTVLIST_CACHE_FILE_DIR, "wb+")) {
		Com_Printf_State(PRINT_FAIL, "Can't create QTVList cache file\n");
        return;
	}

	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);

	res = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
	fclose(f);

	if (res != CURLE_OK) {
		Com_Printf_State(PRINT_FAIL, "Couldn't download QTV list");
		return;
	}
}
static void SB_Update_Source_From_URL(const source_data *s, server_data *servers[],
	int *serversn)
{
	CURL *curl;
	CURLcode res;
	size_t filename_buf_len;
	char *filename;
	FILE *f;
 
	if (s->type != type_url) {
		Com_Printf_State(PRINT_FAIL, "SB_Update_Source_From_URL() Invalid argument\n");
		return;
	}

	curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_URL, s->address.url);
	}
	else {
		Com_Printf_State(PRINT_FAIL, "SB_Update_Source_From_URL() Can't init cURL\n");
		return;
	}
	
	filename_buf_len = SB_URL_To_Filename_Length(s->address.url);
	filename = Q_malloc(filename_buf_len);
	SB_URL_to_FileName(s->address.url, filename, filename_buf_len);
	if (!FS_FCreateFile(filename, &f, "ezquake/sb/cache", "wt+")) {
		Com_Printf_State(PRINT_FAIL, "SB_Update_Source_From_URL() Can't open cached file");
		return;
	}

	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);

	res = curl_easy_perform(curl);
	if (res != CURLE_OK) {
		Com_Printf("Error: Could not read URL %s\n", s->address.url);
	}

	fseek(f, 0, SEEK_SET);
	
	SB_Process_URL_Buffer(f, servers, serversn);
	fclose(f);
	Q_free(filename);

    /* always cleanup */ 
    curl_easy_cleanup(curl);
}
qbool SB_Sources_Dump(void)
{
	FILE *f;
	int i;

	if (!FS_FCreateFile(SOURCES_LIST_FILENAME, &f, "ezquake", "wt")) {
		return false;
	}
 
	for (i = 0; i < sourcesn; i++) {
		sb_source_type_t type = sources[i]->type;

		if (type == type_master || type == type_file || type == type_url) {
			const char *typestr;
			const char *name = sources[i]->name;
			const char *loc;

			if (type == type_master) {
				typestr = "master";
				loc = NET_AdrToString(sources[i]->address.address);
			}
			if (type == type_url) {
				typestr = "url";
				loc = sources[i]->address.url;
			}
			else {
				typestr = "file";
				loc = sources[i]->address.filename;
			}
			
			fprintf(f, "%s \"%s\" %s\n", typestr, name, loc);
		}
	}

	fclose(f);

	return true;
}