/* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * Copyright (C) 1995-2011 Opera Software AS.  All rights reserved.
 *
 * This file is part of the Opera web browser.	It may not be distributed
 * under any circumstances.
 *
 * @author Michal Zajaczkowski (mzajaczkowski)
 */

#ifdef AUTO_UPDATE_SUPPORT
#ifndef PLUGIN_INSTALL_MANAGER_H
#define PLUGIN_INSTALL_MANAGER_H

#include "adjunct/quick_toolkit/windows/DesktopWindow.h"
#include "adjunct/quick/managers/DesktopManager.h"
#include "adjunct/quick/managers/AutoUpdateManager.h"
#include "modules/prefs/prefsmanager/opprefslistener.h"
#include "adjunct/quick/windows/DocumentDesktopWindow.h"
#include "adjunct/quick/WindowCommanderProxy.h"

#ifdef MSWIN
// Platform specific things needed to run async installer process on Windows.
# include <Windows.h>
#endif // MSWIN

#define g_plugin_install_manager	(PluginInstallManager::GetInstance())

class PIM_PluginItem;
class UpdatablePlugin;
class PluginInstallManager;
class PIM_AsyncProcessRunner;
class PIM_AsyncProcessRunnerListener;
class PIM_PluginInstallManagerListener;
class PluginInstallInformation;

/**
 * PIM_PluginInstallManagerListener
 *
 * Allows to listen for missing plugin events generated by the PluginInstallManager.
 */
class PIM_PluginInstallManagerListener
{
public:
	virtual ~PIM_PluginInstallManagerListener() {}

	virtual void OnPluginResolved(const OpStringC& a_mime_type) {};
	virtual void OnPluginAvailable(const OpStringC& a_mime_type) {};
	virtual void OnPluginDownloadStarted(const OpStringC& mime_type) {};
	virtual void OnPluginDownloadCancelled(const OpStringC& mime_type) {};
	virtual void OnPluginDownloaded(const OpStringC& a_mime_type) {};
	virtual void OnPluginInstalledOK(const OpStringC& a_mime_type) {};
	virtual void OnPluginInstallFailed(const OpStringC& a_mime_type) {};
	virtual void OnPluginsRefreshed() {};
	virtual void OnFileDownloadProgress(const OpStringC& a_mime_type, OpFileLength total_size, OpFileLength downloaded_size, double kbps, unsigned long estimate) {};
	virtual void OnFileDownloadDone(const OpStringC& a_mime_type, OpFileLength total_size) {};
	virtual void OnFileDownloadFailed(const OpStringC& a_mime_type) {};
	virtual void OnFileDownloadAborted(const OpStringC& a_mime_type) {};
};

/**
 * PIM_AsyncProcessRunnerListener
 *
 * Allows to listen for the process finished event for a process runner.
 */
class PIM_AsyncProcessRunnerListener
{
public:
	virtual ~PIM_AsyncProcessRunnerListener() {}

	virtual void OnProcessFinished(DWORD exit_code) {}
};

/**
 * PIM_ManagerNotification
 *
 * Values passed to PluginInstallManager::Notify(), allowing to choose the requested notification.
 */
enum PIM_ManagerNotification {
	PIM_PLUGIN_MISSING = 1,
	PIM_PLUGIN_RESOLVED,
	PIM_PLUGIN_DETECTED,
	PIM_INSTALL_DIALOG_REQUESTED,
	PIM_MANUAL_INSTALL_REQUESTED,
	PIM_AUTO_INSTALL_REQUESTED,
	PIM_INSTALL_CANCEL_REQUESTED,
	PIM_INSTALL_OK,
	PIM_INSTALL_FAILED,
	PIM_PLUGIN_DOWNLOAD_REQUESTED,
	PIM_PLUGIN_DOWNLOAD_CANCEL_REQUESTED,
	PIM_PLUGIN_DOWNLOAD_STARTED,
	PIM_PLUGIN_DOWNLOADED,
	PIM_REFRESH_PLUGINS_REQUESTED,
	PIM_REMOVE_WINDOW,
	PIM_INSTALL_DIALOG_CLOSED,
	PIM_SET_DONT_SHOW,
	PIM_ENABLE_PLUGIN_REQUESTED,
	PIM_RELOAD_ACTIVE_WINDOW,
	PIM_PLUGIN_PLACEHOLDER_CLICKED
};

/**
 * PIM_InstallMethod
 *
 * Installation method that is used for the specific plugin, silent or interactive.
 */
enum PIM_InstallMethod {
	PIM_IM_NONE = 1,
	PIM_IM_AUTO,
	PIM_IM_MANUAL
};

/**
 * PIM_DialogMode
 *
 * Dialog mode for the InstallPluginDialog.
 */
enum PIM_DialogMode {
	PIM_DM_NONE,				// "Invalid" initialization value
	PIM_DM_INSTALL,				// "Normal" plugin install mode
	PIM_DM_REDIRECT,			// Redirect to www.opera.com plugins Web site
	PIM_DM_NEEDS_DOWNLOADING,	// Information that the plugin installer needs to be downloaded
	PIM_DM_IS_DOWNLOADING		// Information that the plugin installer is being downloaded currently in the background
};

enum PIM_ItemState
{
	PIMIS_Available,
	PIMIS_Downloading,
	PIMIS_Downloaded,
	PIMIS_DownloadFailed,
	PIMIS_Installed,
	PIMIS_NotAvailable,
	PIMIS_Unknown
};

/**
 * PIM_PluginItem
 *
 * This class represents a missing plugin item. An object of this class is created for each missing plugin that is held by the
 * PluginInstallManager. A missin plugin item is identified by the mimetype of the content that triggered creating the given item,
 * which is considered to be a unique key.
 * 
 * A missing plugin item has the following state flags:
 *  resolved:	determines whether we got any answer from the autoupdate server about the given mimetype;
 *  available:	set to TRUE if the plugin is resolved and the autoupdate server sent information about plugin installer available for download;
 *  downloaded:	TRUE if the plugin installer is downloaded and its checksum matches the checksum sent by the autoupdate server;
 *  installed:	TRUE if a plugin viewer can be found for the given mimetype.
 *
 * A missing plugin item has a install method:
 * 	PIM_IM_AUTO:	Automatic installation, also called "silent", that requires no user interaction.
 *  PIM_IM_MANUAL:	Manual installation, that requires the user to interact with the installer.
 * The autoupdate server may or may not allow automatic installation for the given plugin. In case the automatic installation fails, an attempt is
 * made to install the plugin manually.
 *
 * The plugin install manager will query the autoupdate server about each missing plugin item waiting for a response. If the autoupdate mechanism is
 * busy and can't send the query out to the autoupdate server or the response given by the autoupdate server doesn't contain a plugin element for the
 * given missing plugin item, the retry counter for the item is increased. Maximum retry count is determined by the preference setting
 * PrefsCollectionUI::PluginAutoInstallMaxRetryCountPerItem.
 *
 * The missing plugin item will own an autoupdate resource that is a result of parsing the autoupdate XML response (i.e. the UpdatablePlugin).
 * Running the plugin installer utilizes the PIM_AsyncProcessRunner platform implementation, that allows to run a process in parallel to Opera
 * and monitor it's termination status.
 *
 * A missing plugin item holds a list of DocumentDesktopWindow* elements that contain a content with the given mimetype. This is necessary in order
 * to show the info bars correctly (Missing Plugin Bar, Reload For Plugin Bar).
 */
class PIM_PluginItem:
	public FileDownloadListener
{
public:
	PIM_PluginItem():
		m_was_resolved(FALSE),
		m_available(FALSE),
		m_install_method(PIM_IM_NONE),
		m_resource(NULL),
		m_is_it_flash(FALSE),
		m_downloading(FALSE)
	{
	};

	~PIM_PluginItem();

	/**
	 * SetMimeType()
	 *
	 * Sets mimetype for the given missing plugin item, to be only called once.
	 *
	 * @param mime_type The mimetype to be set.
	 *
	 * @return OP_STATUS saying if setting the m_mime_type member succeded.
	 */
	OP_STATUS			SetMimeType(const OpStringC& mime_type) { return m_mime_type.Set(mime_type); }

	/**
	 * GetMimeType()
	 *
	 * Gets mimetype for the missing plugin item.
	 *
	 * @return The mimetype string.
	 */
	const OpStringC&	GetMimeType() const { return m_mime_type; };

	/**
	 * SetIsResolved()
	 *
	 * Sets the resolved flag for the missing plugin item.
	 */
	void			SetIsResolved() { m_was_resolved = TRUE; }
	/**
	 * GetIsResolved()
	 *
	 * Returns the value of the resolved flag for missing mimetype item.
	 *
	 * @return The value of the resolved flag.
	 */
	BOOL			GetIsResolved() { return m_was_resolved; }

	/**
	 * GetIsAvailable()
	 *
	 * Returns the missing plugin available flag state.
	 *
	 * @return The value of the available flag.
	 */
	BOOL			GetIsAvailable() { return m_available; }

	/**
	 * SetInstallMethod()
	 *
	 * Sets the install method.
	 *
	 * @param method The install method to be set.
	 */
	void			SetInstallMethod(PIM_InstallMethod method) { m_install_method = method; }

	/**
	 * GetInstallMethod()
	 *
	 * Returns the install method set for the plugin item.
	 *
	 * @return The install method.
	 */
	PIM_InstallMethod	GetInstallMethod() { return m_install_method; }

	/**
	 * GetIsDownloaded()
	 *
	 * Checks if the plugin installer (i.e. an exe file on Windows) is present in the filesystem and if its checksum matches
	 * the checksum sent by the autoupdate server.
	 *
	 * @return TRUE if the installer is ready to be run, FALSE if it needs to be downloaded first.
	 */
	BOOL			GetIsDownloaded();

	BOOL			GetIsDownloading();

	/**
	 * GetIsInstalled()
	 *
	 * Checks if a plugin was eventually installed for the mimetype given by the missing plugin item. This is currently determied
	 * by querying the viewers module.
	 *
	 * @return TRUE if the plugin is installed, FALSE otherwise.
	 */
	BOOL			GetIsInstalled();

	BOOL			GetIsItFlash();

	BOOL			GetIsReadyForInstall();

	/**
	 * SetResource()
	 *
	 * Will take the ownership of the passed UpdatablePlugin resource. See updatablefile.h.
	 *
	 * @param resource A pointer to the UpdatablePlugin resource that describes the downloadable plugin installer for this missing plugin item. 
	 */
	void			SetResource(UpdatablePlugin* resource);

	/**
	 * GetResource()
	 *
	 * Will return the resource set with SetResource().
	 *
	 * @return The UpdatablePlugin resource pointer that describes the updatable plugin installer.
	 */
	UpdatablePlugin* GetResource() { return m_resource; };

	/**
	 * InstallPlugin()
	 * 
	 * Starts the plugin installation in the requested mode (automatic/silent, manual).
	 *
	 * @param silent TRUE if we want automatic install (i.e. no user interaction), FALSE if we want manual install.
	 *
	 * @return ERR if automatic install was requested with the silent parameter but the autoupdate server did not allow it
	 *         ERR if running the installer process failed
	 *          OK if running the installer process succeeded
	 */
	OP_STATUS		InstallPlugin(BOOL silent);

	/**
	 * CancelPluginInstallation()
	 *
	 * Requests termination of the installer process, effectivelly calling PIM_AsyncProcessRunner::KillProcess().
	 * The return value relates to the OS API call, like TerminateProcess() on Windows.
	 *
	 * @return ERR If the attempt to terminate the process failed
	            OK If the attempt to terminate the process succeeded
	 */
	OP_STATUS		CancelPluginInstallation();

	/**
	 * AddDesktopWindow()
	 *
	 * Adds a new DesktopWindow pointer to the list held by this missing plugin item.
	 *
	 * @param win The pointer to be added.
	 *
	 * @return ERR if the win parameter was NULL
	 *         ERR If the pointer is already there for this mimetype
	 *         ERR If adding the pointer element to the list failed
	 *          OK Otherwise
	 */
	OP_STATUS		AddDesktopWindow(DesktopWindow* window);

	/**
	* RemoveDesktopWindow()
	*
	* Removed the given DesktopWindow pointer from the list of windows for this mimetype.
	* Must be called when the window is destroyed so that we don't try to dereference an invalid pointer.
	* May be called when we want to unregister the given window from notifications, i.e. when it's URL has changed.
	*
	* @param win The pointer to be removed.
	*
	* @return ERR If removing the pointer failed.
	*          OK Otherwise.
	*/
	OP_STATUS		RemoveDesktopWindow(DesktopWindow* window);

	/**
	 * OnPluginAvailable()
	 *
	 * Iterates through the stored DesktopWindow list and calls OnPluginAvailable() for each of them.
	 * Sets the plugin available flag.
	 * Notifies the window commander for each window that the plugin is available so that the core 
	 * can recieve that notification and update placeholders.
	 * Generates a target filename for the plugin installer and notifying all windows that registered 
	 * themselves as containing a content with a given mimetype, that the plugin is available for download.
	 * It is the responsibility of each DesktopWindow implementation (DesktopGadget, DocumentDesktopWindow)
	 * to implement this method and undertake appropiate actions (i.e. show infobars).
	 *
	 */
	void			OnPluginAvailable();

	/**
	 * OnPluginInstalled()
	 *
	 * Iterates through the stored window list and calls OnPluginInstalled() for each of them.
	 * See also OnPluginAvailable()
	 */
	void			OnPluginInstalled();

	/**
	 * CheckAlreadyDownloadedAndVerified()
	 *
	 * Checks if the plugin installer executable file is downloaded and if its checksum matches the one that the 
	 * autoupdate server sent when asked about the given mimetype.
	 * If this function returns OK, the plugin installer is ready to run, otherwise it needs to be (re)downloaded first.
	 *
	 * @return ERR If the installer file is not there or its checksum is invalid
	 *          OK Otherwise
	 */
	OP_STATUS		CheckAlreadyDownloadedAndVerified();

	/**
	 * StartInstallerDownload()
	 *
	 * Starts the installer download using the link provided by the autoupdate server. Will not redownload the file if it
	 * is already downloaded and its checksum matches the one sent by the autoupdate server.
	 *
	 * @return ERR If the UpdatablePlugin resource is NULL
	 *         ERR If the file is already downloaded and the checksum is OK
	 *         ERR If staring the download failed or other error occurred
	 *          OK If the download was started OK and is in progress
	 */
	OP_STATUS		StartInstallerDownload();

	/**
	 * CancelInstallerDownload()
	 *
	 * Stops the download and deletes the target (partially downloaded) file, if any.
	 *
	 * @return ERR If the UpdatablePlugin resource is NULL or the download failed to stop.
	 *          OK Otherwise.
	 */
	OP_STATUS		CancelInstallerDownload();

	PIM_ItemState	GetCurrentState();

	OP_STATUS GetStringWithPluginName(Str::LocaleString string_id, OpString& ret_string);

	/**
	 * FileDownloadListener implementation, see file_downloader.h
	 */
	void			OnFileDownloadDone(FileDownloader* file_downloader, OpFileLength total_size);
	void			OnFileDownloadFailed(FileDownloader* file_downloader);
	void			OnFileDownloadAborted(FileDownloader* file_downloader);
	void			OnFileDownloadProgress(FileDownloader* file_downloader, OpFileLength total_size, OpFileLength downloaded_size, double kbps, unsigned long time_estimate);

private:
	/**
	 * The mimetype is a unique identifier of a missing plugin
	 */
	OpString						m_mime_type;
	/**
	 * List of DesktopWindow elements that were requesting this plugin, the list is used to notify the elements about plugin
	 * becoming available/installed. Also, the core is notified this way, by getting the window commander for each window.
	 */
	OpVector<DesktopWindow>			m_windows;
	/**
	 * The resolved flag
	 */
	BOOL							m_was_resolved;
	/**
	 * The available flag
	 */
	BOOL							m_available;
	/**
	 * The install method
	 */
	PIM_InstallMethod				m_install_method;
	/**
	 * The UpdatablePlugin resource pointer that will hold a reference to the UpdatablePlugin passed by the 
	 * autoupdate module. Setting the resource pointer via SetResource() means transferring the ownership.
	 */
	UpdatablePlugin*				m_resource;

	BOOL							m_is_it_flash;

	BOOL							m_downloading;
};

/**
 * PluginInstallManager
 *
 * This is the manager that controls the plugin installation mechanism.
 * The PluginInstallManager holds a list of PIM_PluginItem objects, each representing a missing plugin.
 * Most of manager functionality is available via the Notify() method, which drives its behavior.
 * 
 * The PluginInstallManager uses the following preferences:
 *
 *    PrefsCollectionUI::PluginAutoInstallMaxItems
 *       This is the maximum count of PIM_PluginItem objects stored by the manager. If this maximum count is
 *       exceeded, the PluginInstallManager will not attempt to track any new missing plugins. This setting exists so that it is impossible to create
 *       a large number of PIM_PluginItem objects by feeding the browser with a number of URLs leading to pages with content described as having some
 *       random/bogus mimetypes (i.e. "mime_type/abrakadabra9"), which could lead to slowing down the browser or maybe running out of memory.
 *       The PIM_PluginItem list is never cleaned up during a browser session, an item is put out of the list only after a succesful plugin installation.
 *       The current max count of 10 seems to be a reasonable tradeoff between the number of mimetypes tracked and virtually any impact on the performance
 *       introudced by going through the PIM_PluginItem list indexed by the mimetypes.
 *
 *    PrefsCollectionUI::PluginAutoInstallRetrySec
 *       Number of seconds that pass between repeated attempts to resolve mimetypes that are pending a resolve. Setting this too low won't be a good protection
 *       against temporary network/server failures, setting this too might result in user never seeing a retry attempt at all.
 *       First resolve attempt after a new missing plugin item is created is made instantly, so the user doesn't have to wait the retry time to see the Missing
 *       Plugin Toolbar.
 *
 *    PrefsCollectionUI::PluginAutoInstallMaxRetryCountPerItem
 *       Maximum retry count per mimetype when trying to resolve it using the autoupdate server. If the plugin item could not be resolved (i.e. the autoupdate
 *       server did not return any XML response regarding the given mimetype) despite this many attempts, it remains unresolved until the browser is closed.
 *       
 *    PrefsCollectionUI::PluginAutoInstallEnabled
 *       Determines if the feature is active. If this preference is set to 0, this feature will be turned off, effectively not showing any toolbars nor seding
 *       out any plugin resolve requests to the autoupdate server. The plugin placeholders will state that the plugin is unknown regardless of their mimetype
 *       and clicking such a placeholder will result in a dialog offering a redirect to the Opera plugins page (i.e. http://www.opera.com/plugins/?MIMETYPE).
 * 
 */
class PluginInstallManager:
	public OpPrefsListener,
	public DocumentWindowListener,
	public AdditionCheckerListener,
	public DesktopManager<PluginInstallManager>
{
// No need to make the interface methods required by PIM_PluginItem public for everybody.
friend class PIM_PluginItem;

public:

	static const DWORD PIM_PROCESS_DEFAULT_EXIT_CODE = 123;

	PluginInstallManager();
	~PluginInstallManager();

	/**
	 * Init()
	 *
	 * The manager init function.
	 *
	 * @return OP_STATUS after initialization.
	 */
	OP_STATUS		Init();

	/**
	 * Notify()
	 *
	 * Triggers the desired action and returns the return code.
	 * Available actions:

	 * PIM_PLUGIN_MISSING
	 *
	 * Notifies the manager that a plugin is missing for a given mimetype. Used by core when an <EMBED> or <OBJECT> is found that cannot be displayed 
	 * due to lack of plugin. Causes the manager to create a new PIM_PluginItem object and attempt to "resolve" the plugin via the autoupdate server
	 * in order to see if a plugin is available. In case a PIM_PluginItem object has already been created, the manager shows appropiate info bar for 
	 * the window notifying about the missing plugin.
	 *
	 * Params:
	 *   mime_type - the mimetype for which a plugin is missing
	 *   window - the DocumentDesktopWindow* that is notifying about the missing plugin
	 *   resource - NULL 
	 *
	 * PIM_PLUGIN_RESOLVED
	 *
	 * Notifies the manager, that a plugin has been resolved for the given mimetype, meaning that we got some XML back from the autoupdate server for
	 * this mimetype. Causes to transfer the ownership of the passed UpdatablePlugin resource from the caller to the PluginInstallManager. To be called
	 * from the autoupdate module when appropiate. Will set the plugin item as available if needed.
	 *
	 * Params:
	 *   mime_type - the mimetype for which a response XML from the autoupdate server was received
	 *   window - NULL
	 *   resource - the UpdatablePlugin* constructed basing on the XML from autoupdate server
	 *
	 * PIM_INSTALL_DIALOG_REQUESTED
	 *
	 * Notifies the manager that the plugin installation dialog was requested. Currently called from core when the plugin placeholder is clicked as well
	 * as from the platform code when the Plugin Missing Toolbar "Install" button is clicked.
	 * If the passed mimetype referes to a plugin item that is available, the install dialog will allow the installation process to start.
	 * If the plugin item is marked as not available or not resolved or if the mimetype passed is empty, the install dialog will offer opening a Web 
	 * page with information about plugins (i.e. http://www.opera.com/plugins/?MIMETYPE).
	 *
	 * Params:
	 *   mime_type - the mimetype for which the install dialog was requested
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_MANUAL_INSTALL_REQUESTED
	 *
	 * Starts the plugin installer process in non-silent mode, i.e. with the UI visible and needing user interaction. Returns error if the installer
	 * process could not be started. The plugin installer is started using the platform implementation of the PIM_AsyncProcessRuneer class, which 
	 * starts the process and polls it for exit code using some time interval.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer should be run
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_AUTO_INSTALL_REQUESTED
	 *
	 * Starts the plugin installer in silent mode, i.e. with the UI hidden and/or not requiring any user interaction. Utilizes the silent install
	 * parameter passed from the autoupdate server when the plugin is "resolved". Returns error if the autoupdate server forbid silent installation
	 * for the given mimetype or if the installer process failed to start.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer should be run
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_INSTALL_CANCEL_REQUESTED
	 *
	 * Request the platform implementation of PIM_AsyncProcessRunner to kill the running installer process. This is called when user clicks the "Cancel"
	 * button in the plugin install dialog while the plugin installer process is running.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process should be killed
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_INSTALL_OK
	 *
	 * Informs the manager, that a plugin was installed OK for the given mimetype. Causes the reload of plugin store, hiding the Plugin Missing Toolbar(s) and
	 * showing Reload For Plugin Toolbar(s). This is called by the UpdatablePlugin class when the installer process finishes with a zero exit code.
	 * In case a plugin is still not installed (i.e. a plugin viewer still can't be found for the given mimetype even thou the viewer store was refreshed),
	 * this call returns error.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process finished succesfully
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_INSTALL_FAILED
	 *
	 * Informs the manager that the plugin installer process exited with a non-zero shell code, which indicates an error. This is also called when the installer 
	 * process fails to start at all. On Windows Vista/7 this can happen if the user forbid tha UAC elevation.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process finished succesfully
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_PLUGIN_DOWNLOAD_REQUESTED
	 *
	 * Starts the plugin installer download using the URL sent by the autoupdate server.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process finished succesfully
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_PLUGIN_DOWNLOAD_CANCEL_REQUESTED
	 *
	 * Cancels the plugin installer download if it is in progress, this is called when the plugin install dialog starts the plugin download.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process finished succesfully
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_PLUGIN_DOWNLOADED
	 *
	 * Informs the manager that the plugin installer download has been finished and the plugin installer is now ready to run.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process finished succesfully
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_REFRESH_PLUGINS_REQUESTED
	 *
	 * Requests a refresh of the plugin store, i.e. similar to calling "jaascript:navigator.plugins.refresh()". This is done after a plugin installer
	 * process finishes with success, before the plugin installation starts to verify if the plugin was installed by some other browser maybe, ...
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin for which the installer process finished succesfully
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_REMOVE_WINDOW
	 *
	 * This is to be called when a DocumentDesktopWindow* is destroyed or when it's URL changes, so that the page it contains might no more include the
	 * content that is missing a plugin. Should the new URL contain any content that is missing any plugin, the core will renotify the PluginInstallManager
	 * via the PIM_PLUGIN_MISSING notification again. 
	 * This call causes the given DocumentDesktopWindow pointer to be removed from the window list for each missing plugin item.
	 *
	 * Params:
	 *   mime_type - NULL
	 *   window - the DocumentDesktopWindow pointer that is to be removed
	 *   resource - NULL
	 *
	 * PIM_INSTALL_DIALOG_CLOSED
	 *
	 * Informs the PluginInstallManager that the plugin install dialog was closed.
	 *
	 * Params:
	 *   mime_type - the mimetype of the plugin that was being installed
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_SET_DONT_SHOW
	 *
	 * Writes down an ini file setting that ensures that the Plugin Missing Toolbar willnot be shown again for the given mimetype. This utilizes the pim_prefs_folder,
	 * pim_prefs_file, pim_prefs_section and pim_prefs_value consts that identify the file that holds the information.
	 * The placeholder bahavior is not changed by this setting, i.e. clicking a placeholer for content of a given mimetype will call the plugin install dialog to be
	 * shown regardless of this setting.
	 *
	 * Params:
	 *   mime_type - the mimetype that we want to surpress the Plugin Missing Toolbar for
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_ENABLE_PLUGIN_REQUESTED
	 *
	 * Causes the plugin viewer for the given mimetype to become (re)enabled. 
	 *
	 * Params:
	 *   mime_type - the mimetype that we want to (re)enable the plugin viewer for
	 *   window - NULL
	 *   resource - NULL
	 *
	 * PIM_RELOAD_ACTIVE_WINDOW
	 *
	 * Causes the active DesktopWindow to be refreshed (i.e. a OpInputAction::ACTION_RELOAD is sent to it). This is used right after the plugin was
	 * verified to be installed after the plugin installer process exits in order to reload the page with the plugin content shown using the newly installer plugin
	 * viewer. 
	 *
	 * Params:
	 *   mime_type - NULL
	 *   window - NULL
	 *   resource - NULL
	 *
	 *
	 * @param pim_action The notification requested by this call
	 * @param mime_type The mimetype passed within the notification, can be NULL, see params desription for each notification
	 * @param window A DocumentDesktopWindow pointer passed within the notification, can be NULL, see params description for each notification
	 * @param resource An UpdatablePlugin resource pointer passed within the notification, can be NULL, see params description for each notification
	 *
	 * @return An OP_STATUS determining if the requested action succeeded.
	 *
	 */ 
	OP_STATUS			Notify(PIM_ManagerNotification pim_action, const OpStringC& mime_type, DesktopWindow* window = NULL, UpdatableResource* resource = NULL, const PluginInstallInformation* = NULL, const OpStringC& plugin_content_url = UNI_L(""));

	/**
	 * GetItemForMimeType()
	 *
	 * Returns a PIM_PluginItem* for the given mime_type or NULL if there is none.
	 *
	 * @param mime_type the mimetype that we want the PIM_PluginItem pointer for
	 *
	 * @return A pointer to a PIM_PluginItem in case one was found for the given mimetype or NULL if none was found
	 */
	PIM_PluginItem*		GetItemForMimeType(const OpStringC& mime_type);

	/**
	 * GetPluginInfo()
	 *
	 * Returns plugin name and availablity of the plugin installer for download for the given mimetype. Used by the core to deterine whether the plugin
	 * name is available for showing on the missing plugin placeholder.
	 *
	 * @param mime_type The mimetype that we are looking the information for
	 * @param[out] plugin_name The plugin name (i.e. "Adobe Flash Player") for the given mimetype
	 * @param[out] plugin_available Indicated whether the autoupdate server is willing to serve an installer for the plugin for the given mimetype
	 *
	 * @return OP_STATUS for the call.
	 */
	OP_STATUS			GetPluginInfo(const OpStringC& mime_type, OpString& plugin_name, BOOL& plugin_available);

	/**
	 * GetDontShowToolbar()
	 *
	 * Returns TRUE if the Plugin Missing Toolbar should be surpressed for the mimetype. This is used by the "Never for this plugin" toolbar button.
	 *
	 * @param mime_type The mimetype that we want to know the surpress state for
	 *
	 * @return TRUE if the toolbar should be surpressed, FALSE if the toolbar should be shown.
	 */
	BOOL				GetDontShowToolbar(const OpStringC& mime_type);

	/**
	 * AutoUpdateManager::AUM_AdditionListener implementation
	 */
	void OnAdditionResolved(UpdatableResource::UpdatableResourceType type, const OpStringC& key, UpdatableResource* resource);
	void OnAdditionResolveFailed(UpdatableResource::UpdatableResourceType type, const OpStringC& key);

	/**
	 * DocumentWindowListener implementation
	 */
	void OnUrlChanged(DocumentDesktopWindow* document_window, const uni_char* url);
	void OnStartLoading(DocumentDesktopWindow* document_window);
	void OnLoadingFinished(DocumentDesktopWindow* document_window, OpLoadingListener::LoadingFinishStatus, BOOL was_stopped_by_user = FALSE);

	/**
	 * OpPrefsListener implementation
	 */
	void PrefChanged(OpPrefsCollection::Collections id, int pref, int newvalue);
	void PrefChanged(OpPrefsCollection::Collections id, int pref, const OpStringC &newvalue);

	/**
	 * FileDownloadListener implementation
	 */
	void OnFileDownloadProgress(const OpStringC& a_mime_type, OpFileLength total_size, OpFileLength downloaded_size, double kbps, unsigned long time_estimate);
	void OnFileDownloadDone(const OpStringC& a_mime_type, OpFileLength total_size);
	void OnFileDownloadFailed(const OpStringC& a_mime_type);
	void OnFileDownloadAborted(const OpStringC& a_mime_type);

	/**
	 * Listener functions for this classes internal listener
	 */
	OP_STATUS	AddListener(PIM_PluginInstallManagerListener* listener) { return m_listeners.Add(listener); }
	OP_STATUS	RemoveListener(PIM_PluginInstallManagerListener* listener) { return m_listeners.Remove(listener); }
protected:
	/**
	 * IsPluginViewerPresent()
	 *
	 * Checks if a plugin viewer if present for the given mimetype, might force reloading the plugin store before checking.
	 *
	 * @param mime_type The mimetype that we want to check the plugin viewer availablity for
	 * @param reload_viewers If equal to TRUE, the Opera plugin store will be refreshed before checking for the plugin viewer availablity
	 *
	 * @return TRUE if there is a plugin viewer available for the mimetype given, FALSE otherwise
	 */
	BOOL				IsPluginViewerPresent(const OpStringC& mime_type, BOOL reload_viewers = FALSE);

	/**
	 * IsPluginViewerDisabled()
	 *
	 * Checks if a plugin viewer is available AND disabled for the given mimetype. Needed in order to redirect to "opera:plugins" after a 
	 * disabled plugin placeholder has been clicked. To be used in combination with IsPluginViewerPresent() to get the most reliable answer.
	 *
	 * @param mime_type Mimetype that we want to check the plugin disabled state for.
	 * 
	 * @return TRUE if the plguin viewer is available and disabled. FALSE if the plugin viewer is not available or available and enabled.
	 */
	BOOL				IsPluginViewerDisabled(const OpStringC& mime_type);

	/**
	 * IsPluginsEnabledGlobally()
	 *
	 * Checks the PrefsCollectionDisplay::PluginsEnabled preference to see if plugins have been disabled globally.
	 *
	 * @return TRUE if the plugins are disabled globally, FALSE otherwise.
	 */
	BOOL				IsPluginsEnabledGlobally();

	/**
	 * Methods to get pref values trimmed with reasonable boundaries. For description of the preferences see up.
	 */
	UINT32				GetPrefResolverMaxItems()			{ return BindToRange(m_pref_resolver_max_items, 5, 30); }

	/**
	 * The helper functions called from the Notify() dispatcher.
	 */
	OP_STATUS					ProcessPluginMissing(const OpStringC& mime_type, DesktopWindow* window);
	OP_STATUS					ProcessPluginResolved(const OpStringC& a_mime_type, UpdatableResource* a_plugin);
	OP_STATUS					ProcessPluginDetected(const OpStringC& mime_type, DesktopWindow* window);
	OP_STATUS					ProcessInstallDialogRequested(const OpStringC& a_mime_type, const OpStringC& plugin_content_url);
	OP_STATUS					ProcessPluginInstallRequested(const OpStringC& a_mime_type, BOOL a_silent);
	OP_STATUS					ProcessInstallCancelRequested(const OpStringC& a_mime_type);
	OP_STATUS					ProcessPluginDownloadRequested(const OpStringC& a_mime_type);
	OP_STATUS					ProcessPluginDownloadStarted(const OpStringC& mime_type);
	OP_STATUS					ProcessPluginDownloadCancelRequested(const OpStringC& a_mime_type);
	OP_STATUS					ProcessPluginDownloaded(const OpStringC& a_mime_type);
	OP_STATUS					ProcessPluginInstallOK(const OpStringC& a_mime_type);
	OP_STATUS					ProcessPluginInstallFailed(const OpStringC& a_mime_type);
	OP_STATUS					ProcessRemoveWindow(DesktopWindow* a_win);
	OP_STATUS					ProcessRefreshPlugins(const OpStringC& a_mime_type);
	OP_STATUS					ProcessInstallDialogClosed(const OpStringC& a_mime_type);
	OP_STATUS					ProcessSetDontShow(const OpStringC& a_mime_type);
	OP_STATUS					ProcessEnablePluginRequested(const OpStringC& a_mime_type);
	OP_STATUS					ProcessReloadActiveWindow();
	OP_STATUS					ProcessPluginPlaceholderClicked(const OpStringC& mime_type, const PluginInstallInformation* information);

	/**
	 * PIM_PluginInstallManagerListener interface call broadcast helpers
	 */
	void						GenerateOnPluginMissing(const OpStringC& a_mime_type);
	void						GenerateOnPluginResolved(const OpStringC& a_mime_type);
	void						GenerateOnPluginAvailable(const OpStringC& a_mime_type);
	void						GenerateOnPluginDownloadStarted(const OpStringC& mime_type);
	void						GenerateOnPluginDownloadCancelled(const OpStringC& mime_type);
	void						GenerateOnPluginDownloaded(const OpStringC& a_mime_type);
	void						GenerateOnPluginInstalledOK(const OpStringC& a_mime_type);
	void						GenerateOnPluginInstallFailed(const OpStringC& a_mime_type);
	void						GenerateOnPluginsRefreshed();
	void						GenerateOnFileDownloadProgress(const OpStringC& a_mime_type, OpFileLength total_size, OpFileLength downloaded_size, double kbps, unsigned long estimate);
	void						GenerateOnFileDownloadDone(const OpStringC& a_mime_type, OpFileLength total_size);
	void						GenerateOnFileDownloadFailed(const OpStringC& a_mime_type);
	void						GenerateOnFileDownloadAborted(const OpStringC& a_mime_type);

	/**
	 * Helper functions for reading the preferences
	 */
	void						ReadPrefs();
	OP_STATUS					InitPrefsFile();

	/**
	 * SetDontShowToolbar()
	 *
	 * Writes down a setting to surpress showing the Missing Plugin Toolbar for the given mimetype.
	 *
	 * @param mime_type The mimetype we want to surpress showing the toolbar for
	 *
	 * @return OP_STATUS of the operation, it may fail due to probles with writing a setting in the appropiate ini file.
	 */
	OP_STATUS					SetDontShowToolbar(const OpStringC& mime_type);

	/**
	 * ShowInstallPluginDialog()
	 *
	 * Creates and shows the install plugin dialog, initializing it with the parameters requested.
	 *
	 * @param dialog_mode The dialog mode, either "redirect" or "install". The "redirect" dialog mode allows the user to open the Opera knowledge base about plugins.
	 * @param mime_type The mimetype for the plugin dialog which it needs to install the proper plugin/show the proper KB page.
	 *
	 * @return OP_STATUS of the operation.
	 */
	OP_STATUS					ShowInstallPluginDialog(PIM_DialogMode dialog_mode, const OpStringC& mime_type, const OpStringC& information);

	/**
	 * Enables plugin viewers globally/on a per plugin basis for a given mimetype.
	 */
	OP_STATUS					EnablePluginsGlobally();
	OP_STATUS					EnablePluginViewer(const OpStringC& mime_type);

	/** 
	 * Helper function
	 */
	inline int					BindToRange(int val, int min, int max) { return MIN(MAX(val, min), max); }

	/**
	 * This IS ugly.
	 * Due to nasty bugs DSK-329146, DSK-329237, DSK-328346 we musn't open links from the EULA dialog in the same window
	 * as the dialog is over. The dialog is modal and using the browser to followup on any further action while the dialog
	 * is still there will result in a mess.
	 * What we do is we open the link a new window if the OpPageView parent is the plugin install dialog
	 * (see OpPageView::OnPageDownloadRequest()). In order to have it working reliably, the link targets, i.e. "_blank" 
	 * can't be respected, since we won't be able to tell where did the new OpPageView came from without violating the 
	 * WindowCommander API quite seriously.
	 * Therefore these two methods that will set the "Igore Target" preference to TRUE when the plugin install dialog
	 * is opened and restore its value to the previous setting once the dialog is closed.
	 * If the new refactorized window interface allows to check where the new DekstopWindow came from (like 
	 * OpenURLSetting::m_src_window for DesktopWindow) then this can be worked around in some other way and these two 
	 * may be removed.
	 */
	void						SetIgnoreTarget();
	void						RestoreIgnoreTarget();

	/**
	 * Vector holding the PIM_PluginItem elements representing the missing plugins
	 */
	OpVector<PIM_PluginItem>	m_items;

	/**
	 * Needed to manage the INI file holding information about mimetypes for which the Missing Plugin Toolbar is surpressed.
	 */
	OpFile m_prefs_file;
	PrefsFile					m_prefs_pfile;
	BOOL						m_prefs_file_inited;

	/**
	 * The preference values.
	 */ 
	BOOL						m_pref_enabled;
	UINT32						m_pref_resolver_max_items;

	/**
	 * Listeners
	 */
	OpListeners<PIM_PluginInstallManagerListener>	m_listeners;

	/**
	 * See SetIgnoreTarget(), RestoreIgnoreTarget() above.
	 */ 
	BOOL					m_ignore_target_pref;

};

/**
 * PIM_AsyncProcessRunner
 *
 * This is an abstract process runner that allows running a process without blocking Opera while waiting for the process to finish, yet still be able
 * to control it in terms of being able to cancel it, see if it still runs and read the exit code. 
 * The platform implementation of this class may vary as long as it behaves like described.
 */
class PIM_AsyncProcessRunner
{
public:
	PIM_AsyncProcessRunner():
		m_listener(NULL),
		m_is_running(FALSE),
		m_shell_exit_code(PluginInstallManager::PIM_PROCESS_DEFAULT_EXIT_CODE)
	{}

	/**
	 * Destructor
	 *
	 * Don't KillProcess() here. Some installers may require Opera to close while they still remain running
	 * (like Windows Flash Player installer in manual mode)
	 */
	virtual ~PIM_AsyncProcessRunner() {}

	/**
	 * RunProcess()
	 * 
	 * Starts the specified process. A non-blocking call.
	 *
	 * @param app The path to the application to be started
	 * @param params The command line parameter(s) string to be passed to the started process
	 *
	 * @return OpStatus::OK if the process was started succesfully.
	 */
	virtual OP_STATUS RunProcess(const OpStringC& app, const OpStringC& params) { return OpStatus::ERR_NOT_SUPPORTED; };

	/**
	 * KillProcess()
	 *
	 * A non-blocking call causing the process to exit despite its current state. Used to terminate the plugin installation by the "Cancel" button in the
	 * plugin install dialog. According to Adobe, on Windows it is possible that the Flash installer "stucks", this should be able to terminate such a process.
	 *
	 * @return OpStatus::OK if terminating the process succeeded.
	 */ 
	virtual OP_STATUS KillProcess() { return OpStatus::ERR_NOT_SUPPORTED;  };

	/**
	 * GetShellExitCode()
	 *
	 * Returns the shell exit code for the last process run.
	 ***************************************************************
	 * Error codes specific to the Windows Flash Player installer as given by Adobe:
	 *
	 * 0	No error
	 * 1	Unknown error
	 * 2	Self test failed
	 * 1000	Unknown argument passed to installer
	 * 1003	Invalid argument passed to installer
	 * 1004	Payload missing
	 * 1005	Payload damaged
	 * 1006 Payload inconsistent
	 * 1009 Argument requires type
	 * 1011 Other installer in progress
	 * 1012 Need admin rights on XP
	 * 1013	Downgrade error
	 * 1014	Registry damaged
	 * 1015	Unable to delete existing Player
	 * 1016	Unable to delete uninstaller
	 * 1017	Unable to remove Player from registry
	 * 1019	Cannot determine Player path
	 * 1020	Cannot determine uninstalelr path
	 * 1022	Need admin rights Vista
	 * 1023	Unable to create directory
	 * 1024	Unable to write file
	 * 1025	Unable to overwrite in-use file
	 * 1030	Generic file creation error
	 * 1031	Permissions file creation error
	 * 1039	User cancelled
	 ***************************************************************
	 *
	 * @return The shell exit code.
	 */
	virtual INT32 GetShellExitCode() { return m_shell_exit_code; }

	/**
	 * IsRunning()
	 *
	 * Allows to determine if the process is currently running.
	 *
	 * @return TRUE if the process is running, FALSE otherwise.
	 */
	virtual BOOL IsRunning() { return m_is_running; }

	/**
	 * SetListener()
	 *
	 * Allows to set a listener that will be notified about the process state. Only one listener can be set.
	 */
	void	SetListener(PIM_AsyncProcessRunnerListener* listener) {	m_listener = listener; }

protected:

	// How often will the running installer process be polled for exit status
	static inline float ProcessPollInterval() { return 0.5 * 1000; } //half a sec

	// The shell exit code that the installer process will be forced to return when aborted
	static inline int ProcessFailExitCode() { return 111; }
	
	PIM_AsyncProcessRunnerListener*	m_listener;
	BOOL	m_is_running;
	DWORD	m_shell_exit_code;
};

#if defined(MSWIN)

/**
 * Windows implementation of PIM_AsyncProcessRunner
 */

class PIM_WindowsAsyncProcessRunner:
	public PIM_AsyncProcessRunner,
	public OpTimerListener
{
public:
	PIM_WindowsAsyncProcessRunner();

	OP_STATUS	RunProcess(const OpStringC& a_app, const OpStringC& a_params);
	OP_STATUS	KillProcess();
	void		OnTimeOut(OpTimer* timer);

protected:
	void		KickTimer();
	void		OnProcessFinished(DWORD exit_code);

	HANDLE		m_process_handle;
	OpString	m_app_name;
	OpString	m_app_params;
	OpTimer 	m_poll_timer;
};

#elif defined(_MACINTOSH_)
#include "platforms/mac/pi/desktop/MacAsyncProcessRunner.h"
#endif //  defined MSWIN and _MACINTOSH_


#if 0
// use when you want to simulate real installer; e.g. when developing on unix :)
// also: set opera:config#AutoUpdate|AutoupdateServer to http://wro01-09.wroclaw.osa/ 
class PIM_MockAsyncProcessRunner
	: public PIM_AsyncProcessRunner
{
	PIM_AsyncProcessRunner()
		: m_shell_exit_code(0)
		, m_is_running(FALSE)
	{}

	virtual OP_STATUS RunProcess(const OpStringC& app, const OpStringC& params)
	{ 
		if(m_listener)
			m_listener->OnProcessFinished(m_shell_exit_code);
		return OpStatus::OK;
	}

	virtual OP_STATUS KillProcess() { return OpStatus::OK; };
};
#endif // 0

#endif // PLUGIN_INSTALL_MANAGER_H
#endif // AUTO_UPDATE_SUPPORT
