/*
** openurl.library - universal URL display and browser launcher library
** Written by Troels Walsted Hansen <troels@thule.no>
** Placed in the public domain.
**
** Modified by Alfonso Ranieri <alforan@tin.it>
**
** Main module for the library with all API functions.
*/

#include "library_common.h"
#include "library_api.h"
#include "library_prefs.h"
#include "library_util.h"
#include "handler.h"

/**************************************************************************
*
* Global variables.
*
*/

extern struct DosLibrary *DOSBase;
struct Library *UtilityBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *IFFParseBase = NULL;
struct RxsLib *RexxSysBase = NULL;
struct URL_Prefs *Prefs = NULL;
struct SignalSemaphore PrefsSemaphore, HandlerSemaphore;

struct Process *HandlerProcess = NULL;
struct MsgPort *HandlerMsgPort;

/**************************************************************************
*
* SAS/C library functions.
*
*/

LIB int __UserLibInit(REG(a6) struct Library *OpenURLBase)
{
	int retval = 1;
	BPTR seglist;
	struct MsgPort *mp = NULL;
	struct HandlerMsg hm = {0};

	/* run time initialized globals */

	SysBase = *(struct ExecBase **)4;
	InitSemaphore(&PrefsSemaphore);
	InitSemaphore(&HandlerSemaphore);

	/* open libraries */

	if(!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36)))
		goto done;

	if(!(UtilityBase = OpenLibrary("utility.library", 36)))
		goto done;

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36)))
		goto done;

	if(!(IFFParseBase = OpenLibrary("iffparse.library", 36)))
		goto done;

	if(!(RexxSysBase = (struct RxsLib *)OpenLibrary("rexxsyslib.library", 33)))
		goto done;

	/* load prefs */

	if(!LoadPrefs(PREFS_NAME_USE) && !(Prefs = LIB_URL_GetDefaultPrefs()))
		goto done;

	/* load and launch handler */

	if(!(mp = CreateMsgPort()))
		goto done;

	if(!(seglist = LoadSeg("L:OpenURL-Handler")))
	{
		struct EasyStruct es;
		es.es_StructSize   = sizeof(struct EasyStruct);
		es.es_Flags        = 0;
		es.es_Title        = "OpenURL Error";
		es.es_TextFormat   = "openurl.library was unable to load\n"
                           "L:OpenURL-Handler into memory.\n"
                           "Please reinstall.";
		es.es_GadgetFormat = "Ok";
		EasyRequestArgs(NULL, &es, NULL, NULL);
		goto done;
	}

    /*
     * Alfonso:
     * The one and only modification done was adding the tag NP_FreeSeglist, TRUE
     */
	HandlerProcess = CreateNewProcTags(NP_Seglist,     seglist,
	                                   NP_FreeSeglist, TRUE,
	                                   NP_Name,        "OpenURL ARexx Handler",
	                                   NP_Priority,    0,
	                                   NP_StackSize,   4096,
	                                   NP_Input,       NULL,
	                                   NP_Output,      NULL,
	                                   NP_CloseInput,  FALSE,
	                                   NP_CloseOutput, FALSE,
	                                   NP_CurrentDir,  NULL,
	                                   NP_HomeDir,     NULL,
	                                   NP_CopyVars,    FALSE,
	                                   TAG_DONE);

	if(!HandlerProcess)
	{
		UnLoadSeg(seglist);
		goto done;
	}

	/* use a startup msg to synchronize with the handler */

	hm.hm_Msg.mn_ReplyPort = mp;
	hm.hm_Msg.mn_Length    = sizeof(struct HandlerMsg);
	hm.hm_Type             = HMT_STARTUP;
	hm.hm_Semaphore        = &HandlerSemaphore;

	PutMsg(&(HandlerProcess->pr_MsgPort), (struct Message *)&hm);
	WaitPort(mp);
	GetMsg(mp);

	/* check that handler initialized its resources correctly */

	if(!hm.hm_Success)
	{
		HandlerProcess = NULL;
		goto done;
	}

	HandlerMsgPort = hm.hm_MsgPort;
	retval         = 0;

done:
	if(mp) DeleteMsgPort(mp);
	if(retval) __UserLibCleanup(OpenURLBase);
	return(retval);
}

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

LIB void __UserLibCleanup(REG(a6) struct Library *OpenURLBase)
{
	/* shutdown handler process */

	if(!AttemptSemaphore(&HandlerSemaphore))
	{
		/* process is still alive, and holding semaphore.
		   signal it to terminate */

		Signal((struct Task *)HandlerProcess, SIGBREAKF_CTRL_C);
	}
	else ReleaseSemaphore(&HandlerSemaphore);

	/* free other resources */

	if(Prefs) LIB_URL_FreePrefs(Prefs);
	CloseLibrary((struct Library *)RexxSysBase);
	CloseLibrary(IFFParseBase);
	CloseLibrary((struct Library *)IntuitionBase);
	CloseLibrary(UtilityBase);
	CloseLibrary((struct Library *)DOSBase);
}

/**************************************************************************
*
* Library functions available through API.
*
*/

LIB BOOL LIB_URL_OpenA(REG(a0) STRPTR url, REG(a1) struct TagItem *tags)
{
	BOOL retval = FALSE;
	struct List portlist;
	struct MsgPort *mp;
	STRPTR fullurl = NULL, PubScreenName = NULL;
	BOOL Show, ToFront, NewWindow, Launch;
	BOOL http_prepend = FALSE;

	/* some initialization */

	NewList(&portlist);
	ObtainSemaphore(&PrefsSemaphore);

	if(!(mp = CreateMsgPort()))
		goto done;

	/* parse the arguments */

	PubScreenName = (STRPTR)GetTagData(URL_PubScreenName,(ULONG)"Workbench",		tags);
	Show          =   (BOOL)GetTagData(URL_Show,         Prefs->up_DefShow,         tags);
	ToFront       =   (BOOL)GetTagData(URL_BringToFront, Prefs->up_DefBringToFront, tags);
	NewWindow     =   (BOOL)GetTagData(URL_NewWindow,    Prefs->up_DefNewWindow,    tags);
	Launch        =   (BOOL)GetTagData(URL_Launch,       Prefs->up_DefLaunch,       tags);

	/* make a copy of the global list of named ports */

	Forbid();
	retval = CopyList(&portlist, &SysBase->PortList, sizeof(struct Node));
	Permit();

	if(!retval) goto done;

	/* prepend "http://" if URL has no method */

	if(Prefs->up_Flags & UPF_PREPENDHTTP)
	{
		STRPTR colon;

		colon = strchr(url, ':');

		if(!colon)
			http_prepend = TRUE;
		else
		{
			STRPTR p;

			for(p = url; p < colon; p++)
			{
				if(!isalnum(*p) && (*p != '+') && (*p != '-'))
				{
					http_prepend = TRUE;
					break;
				}
			}
		}
	}

	if(http_prepend)
	{
		if(!(fullurl = AllocVec(strlen(url) + 8, MEMF_PUBLIC)))
			goto done;

		SPrintf(fullurl, "http://%s", url);
	}
	else fullurl = url;

	/* mailto: or generic URL? */

	if((Prefs->up_Flags & UPF_DOMAILTO) && !strncmp(url, "mailto:", 7))
		retval = SendToMailer(fullurl, &portlist, mp, Show, ToFront, Launch, PubScreenName);
	else
		retval = SendToBrowser(fullurl, &portlist, mp, Show, ToFront, NewWindow, Launch, PubScreenName);

done:
	ReleaseSemaphore(&PrefsSemaphore);
	FreeList(&portlist, sizeof(struct Node));
	if(mp) DeleteMsgPort(mp);
	if(http_prepend && fullurl) FreeVec(fullurl);
	return(retval);
}

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

LIB struct URL_Prefs *LIB_URL_GetPrefs(VOID)
{
	struct URL_Prefs *p;

	/* make a copy of the prefs structure and return that */

	ObtainSemaphoreShared(&PrefsSemaphore);
	p = CopyPrefs(Prefs);
	ReleaseSemaphore(&PrefsSemaphore);
	return(p);
}

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

LIB VOID LIB_URL_FreePrefs(REG(a0) struct URL_Prefs *up)
{
	/* free a prefs structure */
	FreeList((struct List *)&up->up_BrowserList, sizeof(struct URL_BrowserNode));
	FreeList((struct List *)&up->up_MailerList, sizeof(struct URL_MailerNode));
	FreeMem(up, sizeof(struct URL_Prefs));
}

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

LIB BOOL LIB_URL_SetPrefs(REG(a0) struct URL_Prefs *p, REG(d0) BOOL permanent)
{
	BOOL retval = FALSE;
	struct URL_Prefs *new_p;

	ObtainSemaphore(&PrefsSemaphore);

	/* copy prefs structure */

	if(!(new_p = CopyPrefs(p)))
		goto done;

	if(new_p->up_Version > PREFS_VERSION)
		goto done;

	if(new_p->up_Version < 2)
		SetDefaultPrefsV2(new_p);

	if(new_p->up_Version == 2)
		ConvertPrefsV2toV3(Prefs);

	if(new_p->up_Version < 3)
		SetDefaultPrefsV3(new_p);

	new_p->up_Version = PREFS_VERSION;
	new_p->up_Flags &= ~UPF_ISDEFAULTS;

	LIB_URL_FreePrefs(Prefs);
	Prefs = new_p;

	/* and save it to disk */

	if(!SavePrefs(PREFS_NAME_USE, Prefs)) goto done;
	if(permanent && !SavePrefs(PREFS_NAME_SAVE, Prefs))
		goto done;

	retval = TRUE;
done:
	ReleaseSemaphore(&PrefsSemaphore);
	return(retval);
}


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

LIB struct URL_Prefs *LIB_URL_GetDefaultPrefs(VOID)
{
	struct URL_Prefs *p;

	if(!(p = AllocMem(sizeof(struct URL_Prefs), MEMF_CLEAR)))
		return(NULL);

	p->up_Version = PREFS_VERSION;
	p->up_Flags |= UPF_ISDEFAULTS;

	if(!SetDefaultPrefsV1(p))
	{
		LIB_URL_FreePrefs(p);
		return(NULL);
	}

	SetDefaultPrefsV2(p);

	if(!SetDefaultPrefsV3(p))
	{
		LIB_URL_FreePrefs(p);
		return(NULL);
	}

	return(p);
}

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

LIB BOOL LIB_URL_LaunchPrefsApp(VOID)
{
	char path[256];

	if(GetVar("OpenURL_Prefs_Path", path, sizeof(path), 0) == -1)
		strcpy(path, "SYS:Prefs/OpenURL");

	if(SystemTags(path,
	              SYS_Asynch, TRUE,
	              SYS_Input,  Open("NIL:", MODE_NEWFILE),
	              SYS_Output, NULL,
	              TAG_END) == -1)
		return(FALSE);
	else
		return(TRUE);
}

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

ASM ULONG a0hack(REG(d0) ULONG return1, REG(a0) UBYTE *return2)
{
	/* lame hack to return an argstring in A0 as arexx expects */
	return(return1);
}

LIB ULONG LIB_DoFunction(REG(a0) struct RexxMsg *rxmsg)
{
	UBYTE *argstr;
	STRPTR resstr;

	/* check validity of rexx msg */

	if(!rxmsg || !IsRexxMsg(rxmsg) || !(rxmsg->rm_Action & RXFUNC) ||
	   !rxmsg->rm_Args[0])
	{
		return(ERR10_010); /*  invalid message packet      */
	}

	/* check that we recognise the command */

	if(!stricmp(rxmsg->rm_Args[0], "OPENURL"))
	{
		struct TagItem tags[MAXRMARG + 1];
		STRPTR url = NULL;
		int i, j;

		/* parse the arguments (ReadArgs would be overkill for this) */

		for(i = 1, j = 0; rxmsg->rm_Args[i] && j < MAXRMARG; i++)
		{
			Tag tag;

			if(!stricmp(rxmsg->rm_Args[i], "SHOW") || !stricmp(rxmsg->rm_Args[i], "NOSHOW"))
				tag = URL_Show;
			else if(!stricmp(rxmsg->rm_Args[i], "TOFRONT") || !stricmp(rxmsg->rm_Args[i], "NOTOFRONT"))
				tag  = URL_BringToFront;
			else if(!stricmp(rxmsg->rm_Args[i], "NEWWIN") || !stricmp(rxmsg->rm_Args[i], "NONEWWIN"))
				tag = URL_NewWindow;
			else if(!stricmp(rxmsg->rm_Args[i], "LAUNCH") || !stricmp(rxmsg->rm_Args[i], "NOLAUNCH"))
				tag  = URL_Launch;
			else
			{
				url = rxmsg->rm_Args[i];
				continue;
			}

			tags[j].ti_Tag  = tag;
			tags[j].ti_Data = strnicmp(rxmsg->rm_Args[i], "NO", 2);
			j++;
		}

		tags[j].ti_Tag = TAG_END;

		if(url && LIB_URL_OpenA(url, tags))
			resstr = "1";
		else
			resstr = "0";
	}
	else if(!stricmp(rxmsg->rm_Args[0], "OPENURLPREFS"))
	{
		if(LIB_URL_LaunchPrefsApp())
			resstr = "1";
		else
			resstr = "0";
	}
	else
		return(ERR10_001); /*  program not found	       */

	if(!(argstr = CreateArgstring(resstr, 1)))
		return(ERR10_003); /*  no memory available	       */

	return(a0hack(0, argstr));
}
