/*
** Amster - Navigator
** Copyright © 2000 by Jacob Laursen
**
** Maintained by Jacob Laursen <laursen@myself.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** 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.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <proto/dos.h>
#include <proto/socket.h>
#include <proto/utility.h>

#include <netdb.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <bsdsocket/socketbasetags.h>
#include <error.h>
#include <time.h>

#include <MUI/NListview_mcc.h>
#include <MUI/textinput_mcc.h>

#include "include/config.h"
#include "include/gui.h"
#include "include/info.h"
#include "include/mui.h"
#include "include/navigator.h"
#include "include/prefs.h"
#include "include/share.h"

#include "include/protos.h"
#include "amster_Cat.h"

#define NAPIGATOR_REQ "GET /servers.php?version=108 HTTP/1.0\r\n\r\n"

/* Global variables */

BOOL ServerListChanged = FALSE;

/* Private prototypes */

void LoadServerList(struct NavigatorData *data);
void SaveServerList(struct NavigatorData *data);
void MarkServerOnline(struct NavigatorData *data, char *server, unsigned short port);
MUIF ServerListDisplay(REG(a2) char **array, REG(a1) struct ServerEntry *entry);
MUIF ServerListCompare(REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct NList_CompareMessage *ncm);
MUIF ServerListDestruct(REG(a2) APTR pool, REG(a1) struct ServerEntry *entry);
MUIF ServerString(REG(a0) struct Hook *hook, REG(a1) APTR *contents);
BOOL GetNapigatorList(struct NavigatorData *data);
BOOL get_response(long sockfd, char *buf, int max);
void UpdateFromNapigator(struct NavigatorData *data, char *list);
void NapigatorAdd(struct NavigatorData *data, LONG argarray[]);


MUIF NavigatorDispatch(REG(a0) struct IClass *cl, REG(a2) Object *obj, REG(a1) Msg msg)
{
	struct NavigatorData *data;
	struct ServerEntry *entry;
	char *buf;

	switch (msg->MethodID) {
		case OM_NEW:
			return(NavigatorNew(cl, obj, (APTR)msg));
		case NAVI_CONNECT:
			{
			int port;
			data = INST_DATA(cl, obj);

			if (gui_onlinestate > OFFLINE) nap_logout();
			get(data->ST_Port,   MUIA_String_Integer,  &buf);
			port = (int)buf;
			if (port > 65535) port = 65535;
			get(data->ST_Server, MUIA_String_Contents, &buf);
			if (gui_onlinestate < CONNECTING) nap_login_fromlist(buf, port);
			return NULL;
			}
		case NAVI_GETSERVER:
			data = INST_DATA(cl, obj);
			DoMethod(data->LV_Server, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &entry);
			if (entry) {
				set(data->ST_Server,  MUIA_String_Contents, entry->Name);
				set(data->ST_Port,    MUIA_String_Integer,  entry->Port);
				set(data->ST_Comment, MUIA_String_Contents, entry->Comment);
			}
			return NULL;
		case NAVI_ADDSERVER:
			{
			BOOL selected = FALSE;

			data = INST_DATA(cl, obj);
			DoMethod(data->LV_Server, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &entry);
			if (entry) selected = TRUE;	/* One entry is already selected */

			if (entry = malloc(sizeof(struct ServerEntry))) {
				entry->LastOnline = 0;
				if (selected) {
					/* We clear the fields instead of dublicating selected entry */
					entry->Name = strdup(MSG_NAVIGATOR_NEW);
					entry->Comment = strdup("");
					entry->Port = 8888;
				}
				else {
					get(data->ST_Server, MUIA_String_Contents, &buf);
					entry->Name = strdup(buf);
					get(data->ST_Port, MUIA_String_Integer, &buf);
					entry->Port = (unsigned short)buf;
					get(data->ST_Comment, MUIA_String_Contents, &buf);
					entry->Comment = strdup(buf);
				}
				DoMethod(data->LV_Server, MUIM_NList_InsertSingle, entry, MUIV_NList_Insert_Sorted);
				set(data->LV_Server, MUIA_NList_First, MUIV_NList_Active_Bottom);
/*				DoMethod(data->LV_Server, MUIM_NList_Jump, MUIV_NList_Jump_Active);*/
				ServerListChanged = TRUE;
			}
			}
			return NULL;
		case NAVI_REMOVESERVER:
			data = INST_DATA(cl, obj);
			DoMethod(data->LV_Server, MUIM_NList_Remove, MUIV_NList_Remove_Active);
			ServerListChanged = TRUE;
			return NULL;
		case NAVI_REDRAWSERVER:
			{
			struct ServerEntry *entry;
			int port;
			APTR *contents = (APTR)(((muimsg)msg)->arg1);
			data = INST_DATA(cl, obj);

			DoMethod(data->LV_Server, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &entry);
			if (entry) {
				switch ((u_long)*(contents+1)) {
					case 0:
						entry->Name = strdup(*contents);
						break;
					case 1:
						port = atoi(*contents);
						if (port < 65536) entry->Port = port;
						else entry->Port = 65535;
						break;
					case 2:
						entry->Comment = strdup(*contents);
						break;
				}
				DoMethod(data->LV_Server, MUIM_NList_Redraw, MUIV_NList_Redraw_Active);
				ServerListChanged = TRUE;
			}
			return NULL;
			}
		case NAVI_LOAD:
			data = INST_DATA(cl, obj);
			LoadServerList(data);
			return NULL;
		case NAVI_SAVE:
			data = INST_DATA(cl, obj);
			SaveServerList(data);
			return NULL;
		case NAVI_MARKSERVER:
			data = INST_DATA(cl, obj);
			MarkServerOnline(data, (char *)(((muimsg)msg)->arg1), (unsigned short)(((muimsg)msg)->arg2));
			return NULL;
		case NAVI_GETNAPIGATOR:
			data = INST_DATA(cl, obj);
			GetNapigatorList(data);
			return NULL;
	}
	return(DoSuperMethodA(cl, obj, msg));
}


ULONG NavigatorNew(struct IClass *cl, Object *obj, struct opSet *msg)
{
	static const struct Hook ServerListDispHook = { {NULL, NULL}, &ServerListDisplay,  NULL, NULL };
	static const struct Hook ServerListCompHook = { {NULL, NULL}, &ServerListCompare,  NULL, NULL };
	static const struct Hook ServerListDestHook = { {NULL, NULL}, &ServerListDestruct, NULL, NULL };
	static const struct Hook ServerStringHook   = { {NULL, NULL}, &ServerString,       NULL, NULL };

	struct NavigatorData *data;

	Object *LV_Server, *ST_Server, *ST_Port, *ST_Comment;
	Object *BT_ConnectConnect, *BT_ConnectAdd, *BT_ConnectRemove, *BT_Update;

	if (obj = (Object *)DoSuperNew(cl, obj,
		MUIA_HelpNode, "navi",
		MUIA_Window_Title, MSG_NAVIGATOR_TITLE,
		MUIA_Window_ID, MAKE_ID('N','A','V','I'),
		WindowContents, VGroup,
			Child, LV_Server = NListviewObject,
				MUIA_NList_Input, TRUE,
				MUIA_NListview_NList, NListObject,
					InputListFrame,
					MUIA_NList_ListBackground, MUII_ListBack,
					MUIA_NList_TitleBackground, MUII_ListBack,
					MUIA_NList_Title, TRUE,
					MUIA_NList_Format, "BAR,BAR,BAR,",
					MUIA_NList_DisplayHook, &ServerListDispHook,
					MUIA_NList_CompareHook2, &ServerListCompHook,
					MUIA_NList_DestructHook, &ServerListDestHook,
				End,
			End,
			Child, ColGroup(2),
				Child, Label2(MSG_NAVIGATOR_SERVER),
				Child, HGroup,
					Child, ST_Server = TextinputObject,
						StringFrame,
                        MUIA_Textinput_Multiline, FALSE,
						MUIA_CycleChain, 1,
						MUIA_String_Accept, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-",
					End,
					Child, Label2(MSG_NAVIGATOR_PORT),
					Child, ST_Port = TextinputObject,
						StringFrame,
                        MUIA_Textinput_Multiline, FALSE,
						MUIA_HorizWeight, 30,
						MUIA_String_Accept, "0123456789",
						MUIA_String_MaxLen, 6,
						MUIA_CycleChain, 1,
					End,
				End,
				Child, Label2(MSG_NAVIGATOR_COMMENT),
				Child, ST_Comment = TextinputObject,
					StringFrame,
                    MUIA_Textinput_Multiline, FALSE,
					MUIA_CycleChain, 1,
				End,
			End,
			Child, RectangleObject,
				MUIA_FixHeight, 8,
				MUIA_Rectangle_HBar, TRUE,
			End,
			Child, HGroup,
				Child, BT_ConnectConnect = SimpleButton(MSG_NAVIGATOR_CONNECT_GAD),
				Child, BT_ConnectRemove  = SimpleButton(MSG_NAVIGATOR_REMOVE_GAD),
				Child, BT_ConnectAdd     = SimpleButton(MSG_NAVIGATOR_ADD_GAD),
				Child, HGroup,
					Child, BT_Update     = SimpleButton(MSG_NAVIGATOR_UPDATE_GAD),
						MUIA_ShortHelp, MSG_NAVIGATOR_UPDATE_HELP,
				End,
			End,
		End,
		TAG_MORE, msg->ops_AttrList))
	{
		data = INST_DATA(cl, obj);
		data->LV_Server  = LV_Server;
		data->ST_Server  = ST_Server;
		data->ST_Port    = ST_Port;
		data->ST_Comment = ST_Comment;

		DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, obj, 3, MUIM_Set, MUIA_Window_Open, FALSE);

		DoMethod(LV_Server, MUIM_Notify, MUIA_NList_TitleClick,  MUIV_EveryTime, LV_Server, 4, MUIM_NList_Sort3, MUIV_TriggerValue, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_Both);
		DoMethod(LV_Server, MUIM_Notify, MUIA_NList_TitleClick2, MUIV_EveryTime, LV_Server, 4, MUIM_NList_Sort3, MUIV_TriggerValue, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_2);
		DoMethod(LV_Server, MUIM_Notify, MUIA_NList_SortType,    MUIV_EveryTime, LV_Server, 3, MUIM_Set, MUIA_NList_TitleMark,  MUIV_TriggerValue);
		DoMethod(LV_Server, MUIM_Notify, MUIA_NList_SortType2,   MUIV_EveryTime, LV_Server, 3, MUIM_Set, MUIA_NList_TitleMark2, MUIV_TriggerValue);

		DoMethod(BT_ConnectConnect, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, NAVI_CONNECT);
		DoMethod(BT_ConnectRemove,  MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, NAVI_REMOVESERVER);
		DoMethod(BT_ConnectAdd,     MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, NAVI_ADDSERVER);
		DoMethod(BT_Update,         MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, NAVI_GETNAPIGATOR);
		DoMethod(LV_Server, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, obj, 1, NAVI_CONNECT);

		LoadServerList(data);

		DoMethod(LV_Server, MUIM_Notify, MUIA_NList_Active, MUIV_EveryTime, obj, 1, NAVI_GETSERVER);

		set(ST_Server, MUIA_String_AttachedList, LV_Server);

		DoMethod(ST_Server,  MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, ST_Server, 4, MUIM_CallHook, &ServerStringHook, MUIV_TriggerValue, 0);
		DoMethod(ST_Port,    MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, ST_Server, 4, MUIM_CallHook, &ServerStringHook, MUIV_TriggerValue, 1);
		DoMethod(ST_Comment, MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, ST_Server, 4, MUIM_CallHook, &ServerStringHook, MUIV_TriggerValue, 2);

		return((ULONG)obj);
	}
	return(0);
}


void LoadServerList(struct NavigatorData *data)
{
	BPTR fh;
	char buf[1024];
	int line = 0;
	struct ServerEntry *entry;
	LONG argarray[] = { NULL, NULL, NULL, NULL };
	UBYTE *argstr = "HOST/A,PORT/A/N,LASTUSED/A/N,COMMENT/K";
	struct RDArgs *rdargs;

	if (fh = Open("PROGDIR:Amster.servers", MODE_OLDFILE)) {
		set(data->LV_Server, MUIA_NList_Quiet, MUIV_NList_Quiet_Full);
		if (rdargs = AllocDosObject(DOS_RDARGS, NULL)) {
			while (FGets(fh, buf, sizeof(buf))) {
				line++;
				
				rdargs->RDA_Buffer = NULL;
				rdargs->RDA_Source.CS_Buffer = buf;
				rdargs->RDA_Source.CS_Length = strlen(buf);
				rdargs->RDA_Source.CS_CurChr = 0;

				argarray[3] = 0;
				if (ReadArgs(argstr, argarray, rdargs)) {
					if (entry = malloc(sizeof(struct ServerEntry))) {
						entry->Name = strdup((char *)argarray[0]);
						entry->Port = *((long *)argarray[1]);
						entry->LastOnline = *((long *)argarray[2]);
						if (argarray[3]) entry->Comment = strdup((char *)argarray[3]);
						else entry->Comment = strdup("");
						entry->Ping = -1;
						DoMethod(data->LV_Server, MUIM_NList_InsertSingle, entry, TAG_DONE);
					}
					FreeArgs(rdargs);
				}
				else gui_debugf((char *)MSG_PARSE_ERROR, "PROGDIR:Amster.servers", line);
			}
			FreeDosObject(DOS_RDARGS, rdargs);
		}
		set(data->LV_Server, MUIA_NList_Quiet, MUIV_NList_Quiet_None);
		Close(fh);
		DoMethod(data->LV_Server, MUIM_NList_Sort);
	}
}


void SaveServerList(struct NavigatorData *data)
{
	struct ServerEntry *entry;
	BPTR fh;
	char buf[1024], *tmp;
	int i;

	fh = Open("PROGDIR:Amster.servers", MODE_NEWFILE);
	if (!fh) return;

	for (i=0; ; i++) {
		DoMethod(data->LV_Server, MUIM_NList_GetEntry, i, &entry);
		if (!entry) break;
		if (entry->Comment && entry->Comment[0] != '\0') {
			if (tmp = strrep(entry->Comment, "\"", "*\"")) {
				sprintf(buf, "%s %ld %ld COMMENT \"%s\"\n", entry->Name, entry->Port, entry->LastOnline, tmp);
				free(tmp);
			}
			else sprintf(buf, "%s %ld %ld\n", entry->Name, entry->Port, entry->LastOnline);
		}
		else sprintf(buf, "%s %ld %ld\n", entry->Name, entry->Port, entry->LastOnline);
		Write(fh, buf, strlen(buf)); /* Should be buffered, check FWrite */
	}

	Close(fh);
	ServerListChanged = FALSE;
}


void MarkServerOnline(struct NavigatorData *data, char *server, unsigned short port)
{
	struct ServerEntry *entry=0;
	int i;
	BOOL new = FALSE;

	for (i=0; ; i++) {
		DoMethod(data->LV_Server, MUIM_NList_GetEntry, i, &entry);
		if (!entry) break;
		if ((strcmp(server, entry->Name) == 0) && (port == entry->Port)) break;
	}
	if (!entry) {	/* Server not already on list */
		if (prf->ServerList < 2) return;
		if (entry = malloc(sizeof(struct ServerEntry))) {
			entry->Name = strdup(server);
			entry->Port = port;
			entry->LastOnline = 0;	/* In case we don't succeed later on... */
			entry->Comment = strdup("");
			entry->Ping = -1;
			new = TRUE;
		}
	}

	entry->LastOnline = GetDateStamp();
	if (new)
		DoMethod(data->LV_Server, MUIM_NList_InsertSingle, entry, MUIV_NList_Insert_Sorted);
	else
		DoMethod(data->LV_Server, MUIM_NList_Redraw, i);
	ServerListChanged = TRUE;
}


MUIF ServerListDisplay(REG(a2) char **array, REG(a1) struct ServerEntry *entry)
{
	static struct DateTime dt;
	static char buf[LEN_DATSTRING*2+6];
	static char	buf2[20];
	static char port[6];
	static char datestring[LEN_DATSTRING];
	static char timestring[LEN_DATSTRING];

	if (entry) {
		*array++ = entry->Name;
		sprintf(port, "%ld", entry->Port);
		*array++ = port;
		if (entry->LastOnline == 0) *array++ = "\33r-";
		else {
				dt.dat_Stamp.ds_Days   = (entry->LastOnline)/(60*60*24);
				dt.dat_Stamp.ds_Minute = ((entry->LastOnline)%(60*60*24))/60;
				dt.dat_Stamp.ds_Tick   = ((entry->LastOnline)%60)*TICKS_PER_SECOND;
				dt.dat_Format = FORMAT_DOS;
				dt.dat_Flags = DTF_SUBST;
				dt.dat_StrDay = NULL;
				dt.dat_StrDate = datestring;
				dt.dat_StrTime = timestring;
				if (DateToStr(&dt)) {
					sprintf(buf, "\33r%s %s", datestring, timestring);
					*array++ = buf;
				}
				else *array++ = "\33r-";
		}
		*array++ = entry->Comment;

		if (entry->Ping >= 0) {
			sprintf(buf2, "%ld", entry->Ping);
			*array   = buf2;
		}
		else *array = "-";
	}
	else {
		*array++ = (char *)MSG_NAVIGATOR_LIST_SERVER;
		*array++ = (char *)MSG_NAVIGATOR_LIST_PORT;
		*array++ = (char *)MSG_NAVIGATOR_LIST_LASTONLINE;
		*array++ = (char *)MSG_NAVIGATOR_LIST_COMMENT;
		*array   = (char *)MSG_NAVIGATOR_LIST_PING;
	}

	return 0;
}


MUIF ServerListCompare(REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct NList_CompareMessage *ncm)
{
	struct ServerEntry *entry1 = ncm->entry1;
	struct ServerEntry *entry2 = ncm->entry2;
	LONG col1 = ncm->sort_type & MUIV_NList_TitleMark_ColMask;
	LONG col2 = ncm->sort_type2 & MUIV_NList_TitleMark2_ColMask;
	ULONG result = 0;

	if (ncm->sort_type == MUIV_NList_SortType_None) return (0);

	if (col1 == 0) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = (LONG) stricmp(entry2->Name, entry1->Name);
		else
			result = (LONG) stricmp(entry1->Name, entry2->Name);
	}
	else if (col1 == 1) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = entry2->Port - entry1->Port;
		else
			result = entry1->Port - entry2->Port;
	}
	else if (col1 == 2) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = entry2->LastOnline - entry1->LastOnline;
		else
			result = entry1->LastOnline - entry2->LastOnline;
	}
	else if (col1 == 3) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = (LONG) stricmp(entry2->Comment, entry1->Comment);
		else
			result = (LONG) stricmp(entry1->Comment, entry2->Comment);
	}
	else if (col1 == 4) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = entry2->Ping - entry1->Ping;
		else
			result = entry1->Ping - entry2->Ping;
	}

	if ((result != 0) || (col1 == col2)) return (result);

	if (col2 == 0) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = (LONG) stricmp(entry2->Name, entry1->Name);
		else
			result = (LONG) stricmp(entry1->Name, entry2->Name);
	}
	else if (col2 == 1) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = entry2->Port - entry1->Port;
		else
			result = entry1->Port - entry2->Port;
	}
	else if (col2 == 2) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = entry2->LastOnline - entry1->LastOnline;
		else
			result = entry1->LastOnline - entry2->LastOnline;
	}
	else if (col2 == 3) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = (LONG) stricmp(entry2->Comment, entry1->Comment);
		else
			result = (LONG) stricmp(entry1->Comment, entry2->Comment);
	}
	else if (col2 == 4) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = entry2->Ping - entry1->Ping;
		else
			result = entry1->Ping - entry2->Ping;
	}

	return (result);
}


MUIF ServerListDestruct(REG(a2) APTR pool, REG(a1) struct ServerEntry *entry)
{
	free(entry);
	return 0;
}


MUIF ServerString(REG(a0) struct Hook *hook, REG(a1) APTR *contents)
{
	DoMethod(gui->WI_Navigator, NAVI_REDRAWSERVER, contents);
	return 0;
}

/* The rest of this file is pure, buggy, untested, experimental crap */

BOOL GetNapigatorList(struct NavigatorData *data)
{
	char *buffer;
	int s;
	struct sockaddr_in sin;
	struct sockaddr *sa;
	struct hostent *he;
	BOOL HasBase;

	if (!SocketBase) {
		if (!(SocketBase = OpenLibrary("bsdsocket.library", 0))) return FALSE;
		HasBase = FALSE;
	}
	else HasBase = TRUE;

	he = gethostbyname("napigator.com");
	if (he == NULL) {
		if (!HasBase) {
			CloseLibrary(SocketBase);
			SocketBase = NULL;
		}
		return FALSE;
	}

	sin.sin_family = AF_INET;
	sin.sin_len = sizeof(sin);
	sin.sin_addr.s_addr = *(long *)he->h_addr_list[0];
	sin.sin_port = htons(80);
	sa = (struct sockaddr *) &sin;

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s < 0) {
		if (!HasBase) {
			CloseLibrary(SocketBase);
			SocketBase = NULL;
		}
		return FALSE;
	}

	if (connect(s, sa, sa->sa_len) < 0) {
		CloseSocket(s);
		if (!HasBase) {
			CloseLibrary(SocketBase);
			SocketBase = NULL;
		}
		return FALSE;
	}

	if (send(s, NAPIGATOR_REQ, sizeof(NAPIGATOR_REQ), 0) != sizeof(NAPIGATOR_REQ)) {
		CloseSocket(s);
		if (!HasBase) {
			CloseLibrary(SocketBase);
			SocketBase = NULL;
		}
		return FALSE;
	}

	if (!(buffer = malloc(65536))) {
		CloseSocket(s);
		if (!HasBase) {
			CloseLibrary(SocketBase);
			SocketBase = NULL;
		}
	}

	if (get_response(s, buffer, 65536)) {
		UpdateFromNapigator(data, buffer);
	}

	CloseSocket(s);
	if (!HasBase) {
		CloseLibrary(SocketBase);
		SocketBase = NULL;
	}
	free(buffer);

	return TRUE;
}


BOOL get_response(long sockfd, char *buf, int max)
{
	int len = 0, le;

	while (len < max-1) {
		le = recv(sockfd, buf+len, max-len-1, 0);
		if (le == 0) {
			buf[len] = '\0';
			return TRUE;
		}
		if (le < 0) {
			return FALSE;
		}
		len += le;
	}
	buf[max-1] = '\0';

	return TRUE;
}


void UpdateFromNapigator(struct NavigatorData *data, char *list)
{
	char *p, *buf;
	int size = strlen(list);
	int i;
	LONG argarray[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
	UBYTE *argstr = "SERVER/A,PORT/A/N,NETWORK/A,USERS/A/N,SONGS/A/N,GBYTES/A/N,COMMENT/A";
	struct RDArgs *rdargs;

	/* Divide it into substrings */
	for (i = 0; i < size; i++) {
		if (list[i] == '\n') list[i] = '\0';
	}

	i = 0;

	if (rdargs = AllocDosObject(DOS_RDARGS, NULL)) {
		while (i < size) {
			p = list+i;

			buf = malloc(strlen(p)+2);
			strcpy(buf, p);
			buf[strlen(p)] = '\n';
			buf[strlen(p)+1] = '\0';

			rdargs->RDA_Buffer = NULL;
			rdargs->RDA_Source.CS_Buffer = buf;
			rdargs->RDA_Source.CS_Length = strlen(buf);
			rdargs->RDA_Source.CS_CurChr = 0;

			if (ReadArgs(argstr, argarray, rdargs)) {
				NapigatorAdd(data, argarray);
				FreeArgs(rdargs);
			}

			free(buf);

			i += strlen(p);
			if (list[i] == '\0') i++;
		}
		FreeDosObject(DOS_RDARGS, rdargs);
	}
}


void NapigatorAdd(struct NavigatorData *data, LONG argarray[])
{
	struct ServerEntry *entry;
	BOOL found = FALSE;
	int j, total;
	u_long tmp;

	GetAttr(MUIA_NList_Entries, data->LV_Server, &tmp);
	total = tmp;

	j = 0;
	while (j<total && !found) {
		DoMethod(data->LV_Server, MUIM_NList_GetEntry, j, &entry);
		if (entry) {
			if (strcmp((char *)argarray[0], entry->Name) == 0 && *((long *)argarray[1]) == entry->Port) found = TRUE;
		}
		j++;
	}

	if (!found) {	/* Server not already on list */
		if (entry = malloc(sizeof(struct ServerEntry))) {
			entry->Name = strdup((char *)argarray[0]);
			entry->Port = *((long *)argarray[1]);
			entry->LastOnline = 0;
			entry->Comment = malloc(strlen((char *)argarray[2]) + strlen((char *)argarray[6]) + 3);
			sprintf(entry->Comment, "%s; %s", (char *)argarray[2], (char *)argarray[6]);
			DoMethod(data->LV_Server, MUIM_NList_InsertSingle, entry, MUIV_NList_Insert_Bottom);
			ServerListChanged = TRUE;
		}
	}

}
