// -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-

//
//  Copyright (C) 2002-2003 Hiroyuki Ikezoe
//  Copyright (C) 2003 Takuro Ashie
//
//  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, 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.
//

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#include <gtkmozembed.h>
#include <nsCOMPtr.h>
#include <nsXPCOM.h>
#include <nsIGenericFactory.h>
#include <nsIComponentRegistrar.h>
#define MOZILLA_INTERNAL_API
#include <nsIServiceManager.h>
#undef MOZILLA_INTERNAL_API
#include <nsIPasswordManager.h>
#include <nsCPasswordManager.h>
#ifdef HAVE_NSIPASSWORD_H
#include <nsIPassword.h>
#endif
#include <nsIIDNService.h>

#include "mozilla.h"
#include "mozilla-prefs.h"
#include "kz-mozfilepicker.h"
#include "kz-mozlauncher.h"
#include "kz-mozprogresslistener.h"
#include "kz-mozprintingpromptservice.h"
#include "kz-profile.h"
#include "kz-proxy-item.h"
#include "kazehakase.h"
#include "kz-password-manager-dialog.h"
#include "kz-mozhistorysearch.h"
#include "kz-mozselectionlistener.h"
#include "GtkNSSDialogs.h"
#include "GtkPromptService.h"


NS_GENERIC_FACTORY_CONSTRUCTOR(KzFilePicker)
NS_GENERIC_FACTORY_CONSTRUCTOR(KzContentHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(KzMozProgressListener)
NS_GENERIC_FACTORY_CONSTRUCTOR(KzMozHistorySearchProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(KzMozPrintingPromptService)
NS_GENERIC_FACTORY_CONSTRUCTOR(GtkPromptService)
NS_GENERIC_FACTORY_CONSTRUCTOR(GtkNSSDialogs)

static const nsModuleComponentInfo sAppComps[] = {
	{
		KZ_DOWNLOAD_CLASSNAME,
		KZ_DOWNLOAD_CID,
#ifdef HAVE_NSITRANSFER_H
		NS_TRANSFER_CONTRACTID,
#else
		NS_DOWNLOAD_CONTRACTID,
#endif
		KzMozProgressListenerConstructor
	},
	{
		KZ_FILEPICKER_CLASSNAME,
		KZ_FILEPICKER_CID,
		KZ_FILEPICKER_CONTRACTID,
		KzFilePickerConstructor
	},
	{
		NS_IHELPERAPPLAUNCHERDLG_CLASSNAME,
		KZ_CONTENTHANDLER_CID,
		NS_IHELPERAPPLAUNCHERDLG_CONTRACTID,
		KzContentHandlerConstructor
	},
	{
		KZ_HISTORY_SEARCH_PROTOCOLHANDLER_CLASSNAME,
		KZ_HISTORY_SEARCH_PROTOCOLHANDLER_CID,
		KZ_HISTORY_SEARCH_PROTOCOLHANDLER_CONTRACTID,
		KzMozHistorySearchProtocolHandlerConstructor
	},
	{
		KZ_PRINTINGPROMPTSERVICE_CLASSNAME,
		KZ_PRINTINGPROMPTSERVICE_CID,
		KZ_PRINTINGPROMPTSERVICE_CONTRACTID,
		KzMozPrintingPromptServiceConstructor
	},
	{
		GTK_NSSDIALOGS_CLASSNAME,
		GTK_NSSDIALOGS_CID,
		NS_BADCERTLISTENER_CONTRACTID,
		GtkNSSDialogsConstructor
	},
	{
		GTK_NSSDIALOGS_CLASSNAME,
		GTK_NSSDIALOGS_CID,
		NS_CERTIFICATEDIALOGS_CONTRACTID,
		GtkNSSDialogsConstructor
	},
  	{
		"Prompt Service",
    		NS_PROMPTSERVICE_CID,
    		"@mozilla.org/embedcomp/prompt-service;1",
    		GtkPromptServiceConstructor
	},
};
static const int sNumAppComps = G_N_ELEMENTS(sAppComps);

#ifndef HAVE_NSIDOCSHELL_GETSECUREUI
/* bee4dc31-1326-4c08-9bda-7b230dca97e9 */
#define NEW_CID { 0xbee4dc31, 0x1326, 0x4c08, { 0x9b, 0xda, 0x7b, 0x23, 0x0d, 0xca, 0x97, 0xe9 } }

static nsresult
reregister_secure_browser_ui (nsIComponentManager *cm, nsIComponentRegistrar *cr)
{
	nsresult rv;
	
	/* Workaround as a result of:
	 *  https://bugzilla.mozilla.org/show_bug.cgi?id=94974
	 * see
	 *  http://bugzilla.gnome.org/show_bug.cgi?id=164670
	 */
	nsCOMPtr<nsIFactory> factory;
	rv = cm->GetClassObjectByContractID ("@mozilla.org/secure_browser_ui;1", 
					     NS_GET_IID(nsIFactory), getter_AddRefs (factory));
	NS_ENSURE_SUCCESS (rv, rv);

	nsCID *cidPtr = nsnull;
	rv = cr->ContractIDToCID("@mozilla.org/secure_browser_ui;1", &cidPtr);
	NS_ENSURE_SUCCESS (rv, rv);

	rv = cr->UnregisterFactory (*cidPtr, factory);
	NS_ENSURE_SUCCESS (rv, rv);

	const nsCID new_cid = NEW_CID;
	rv = cr->RegisterFactory (new_cid, "Kazehakase Secure Browser Class",
				  "@kazehakase.sf.jp/hacks/secure-browser-ui;1", factory);
	NS_ENSURE_SUCCESS (rv, rv);

	return NS_OK;
}
#endif

static gboolean
mozilla_register_components (void)
{
	gboolean ret = TRUE;
	nsresult rv;

	nsCOMPtr<nsIComponentRegistrar> cr;
	rv = NS_GetComponentRegistrar(getter_AddRefs(cr));
	NS_ENSURE_SUCCESS(rv, rv);

	nsCOMPtr<nsIComponentManager> cm;
	NS_GetComponentManager (getter_AddRefs (cm));
	NS_ENSURE_TRUE (cm, FALSE);

	for (int i = 0; i < sNumAppComps; i++)
	{
		nsCOMPtr<nsIGenericFactory> componentFactory;
		rv = NS_NewGenericFactory(getter_AddRefs(componentFactory),
					  &(sAppComps[i]));
		if (NS_FAILED(rv))
		{
			ret = FALSE;
			continue;  // don't abort registering other components
		}

		rv = cr->RegisterFactory(sAppComps[i].mCID,
					 sAppComps[i].mDescription,
					 sAppComps[i].mContractID,
					 componentFactory);
		if (NS_FAILED(rv))
			ret = FALSE;
	}

#ifndef HAVE_NSIDOCSHELL_GETSECUREUI
        // Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=164670
        reregister_secure_browser_ui (cm, cr);
#endif
	return ret;
}


static void 
destroy_browser_cb (GtkMozEmbed *embed, GtkWidget *transient_window)
{
	gtk_widget_destroy(GTK_WIDGET(transient_window));
}


static void
new_window_orphan_cb (GtkMozEmbedSingle *embed, GtkMozEmbed **retval,
		      guint chromemask, gpointer data)
{
	// FIXME! but I don't have any nice idea...
	GtkWidget *newWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	GtkMozEmbed *newBrowser = GTK_MOZ_EMBED(gtk_moz_embed_new());

	gtk_container_add(GTK_CONTAINER(newWindow), GTK_WIDGET(newBrowser));
	g_signal_connect(newBrowser,"destroy_browser",
			 G_CALLBACK(destroy_browser_cb),newWindow);
	*retval = GTK_MOZ_EMBED(newBrowser);
}


static void
cb_profile_changed (KzProfile *profile,
		    const gchar *section,
		    const gchar *key,
		    const gchar *old_value,
		    gpointer data)
{
	gchar proxy_name[1024];
	gboolean use_proxy = FALSE;

	switch (key[0])
	{
	case 'o': // override_user_agent
		break;
	case 'p':
		if (strcmp(key, "proxy_name") == 0 &&
		    KZ_CONF_GET(section, key, proxy_name, STRING))
		{
			KzProxyItem *item = kz_proxy_find(proxy_name);
			if (item) {
				mozilla_prefs_set_proxy(item);
				g_object_unref(G_OBJECT(item));
			}
		}
		break;
	case 'u': // user_agent or use_proxy
		if (strcmp(key, "use_proxy") == 0 &&
		    KZ_CONF_GET(section, key, use_proxy, BOOL))
		{
			mozilla_prefs_set_use_proxy(use_proxy);
		}
		break;
	}
}


extern "C" void
mozilla_init (KzProfile *profile)
{
	GtkMozEmbedSingle *single = NULL;

	mozilla_prefs_init();

	gtk_moz_embed_set_comp_path(MOZILLA_HOME);

	gtk_moz_embed_push_startup();

	mozilla_prefs_set(profile);

	//
	// get the singleton object and hook up to its new window callback
	// so we can create orphaned windows.
	//
	single = gtk_moz_embed_single_get();
	if (!single) {
		g_error("Failed to get singleton embed object!");
		return;
	}

	g_signal_connect(single, "new_window_orphan",
			 G_CALLBACK(new_window_orphan_cb), NULL);

	g_signal_connect(profile, "changed::Global",
			 G_CALLBACK(cb_profile_changed), NULL);


	mozilla_register_components();
}


extern "C" void
mozilla_exit (void)
{
	gtk_moz_embed_pop_startup();
}


extern "C" gboolean
mozilla_get_passwords (GList **passwords)
{
        nsresult result = NS_ERROR_FAILURE;

        nsCOMPtr<nsIPasswordManager> passwordManager =
                        do_GetService(NS_PASSWORDMANAGER_CONTRACTID);
        nsCOMPtr<nsISimpleEnumerator> passwordEnumerator;
        result = passwordManager->GetEnumerator(getter_AddRefs(passwordEnumerator));
        if (NS_FAILED(result)) return FALSE; 

        PRBool enumResult;
        for (passwordEnumerator->HasMoreElements(&enumResult) ;
             enumResult == PR_TRUE ;
             passwordEnumerator->HasMoreElements(&enumResult))
        {
                nsCOMPtr<nsIPassword> nsPassword;
                result = passwordEnumerator->GetNext 
                                        (getter_AddRefs(nsPassword));
                if (NS_FAILED(result)) return FALSE;

                PasswordInfo *p = g_new0 (PasswordInfo, 1);

                nsEmbedCString transfer;
                nsPassword->GetHost (transfer);
                p->host = g_strdup (transfer.get());

		nsEmbedString unicodeName;
		nsPassword->GetUser (unicodeName);

		nsEmbedCString userName;
		NS_UTF16ToCString(unicodeName,
				  NS_CSTRING_ENCODING_UTF8, userName);
		p->username = g_strdup(userName.get());

		*passwords = g_list_prepend (*passwords, p);
        }       

	*passwords = g_list_reverse (*passwords);

	return TRUE;
}


gboolean
mozilla_remove_passwords (GList *passwords)
{
	nsresult result = NS_ERROR_FAILURE;
        nsCOMPtr<nsIPasswordManager> passwordManager =
                        do_GetService(NS_PASSWORDMANAGER_CONTRACTID);

	nsCOMPtr<nsIIDNService> idnService
		(do_GetService ("@mozilla.org/network/idn-service;1"));


        for (GList *l = passwords; l != NULL; l = l->next)
        {
                PasswordInfo *p = (PasswordInfo *)l->data;
		nsresult rv;
		nsEmbedCString host;
		rv = idnService->ConvertUTF8toACE (nsEmbedCString(p->host), host);
		
		nsEmbedString userName;
		NS_CStringToUTF16(nsEmbedCString(p->username), 
				  NS_CSTRING_ENCODING_UTF8, userName);
		
		result = passwordManager->RemoveUser(host, userName);
                if (NS_FAILED(result)) return FALSE;
        }
	
        return TRUE;
}

