# Preferences download module

## Introduction

The Preference download is a layer on top of Host overrides. It's purpose is to download (Site Specific) Preferences from a server hosted by Opera.

It replaces site specific behavior previously hard-coded in Opera.

The functionality of this subsystem is depending on the feature `FEATURE_PREFS_DOWNLOAD`. The Preference download system can also download utility files used by the preferences. This is depending on the tweak `TWEAK_PREFS_FILE_DOWNLOAD`. If this tweak is not activated, Opera should disregard file preferences.

The prefererences received are coded in XML. The XML format is parsed and the new preferences enterred into the preference storage with the prefs module API.

Preference download described in developers Wiki.

## Interface overview and API documentation

### API documentation

The API documentation is extracted automatically by Doxygen.

### Overview

The API consists of the class PrefsLoadManager and the global object, g_PrefsLoadManager. This object is created automatically by the prefsloader module initialization.

The downloading is triggered by document handling code when it receives a resource with the appropriate MIME type: `application/x-opera-configuration-siteprefs`, or by initialization or ui code. `PrefsLoadManager` has a public method `InitLoader` which comes in to flavours:

      void InitLoader(const URL url, OpEndChecker * end = NULL);
      void InitLoader(const OpStringC host, OpEndChecker * end = NULL);

After calling InitLoader the rest should be automatic. The content of the url is downloaded, parsed and set in the normal preferences. But the documents in cache will have to reloaded or reflowed for any effect to be visible. The process can be stopped by the calling code by implementing the virtual class `OpEndChecker`, containing a method `IsEnd` that returns `TRUE` if the parsing should stop. Typically if the user decides not to include the new preferences.

## Use-cases

### Download from previously opened url

Use `PrefsLoadManager::InitLoader(URL)` to download preferences from an open URL, typically when someone has clicked a link of the apropriate MIME type.

Example: The dochand code triggered by the prefsload mime type.

       #ifdef PREFS_DOWNLOAD
         case URL_PREFS_CONTENT:
           {
             g_PrefsLoadManager->InitLoader(current_url, new EndChecker());
             …

### Download from default url

Use `PrefsLoadManager::InitLoader(const OpStringC host)` to open a url to a predefined server and fetch preferences valid for one or more particular sites. In this case the name of the url can be found in the preference `Preference URL` in the `Install` section. This preference is hard-coded and not present in the Opera.ini file.

The `host` argument is used to inform the preference server of what particular site the the browser want preference overloading for. If the argument is empty or a NULL string, server should return preference information about all hosts that it has registered.

Example: gogi desktop ui code downloading preferences from the default server.

       #ifdef PREFS_DOWNLOAD
         // Download User-Agent settings

         class AcceptPrefDownload: public OpEndChecker
         {
         public:
           AcceptPrefDownload(GOGIUI_WindowBar *w) : m_winbar(w) {}
               /**
                * Always accept spoof lists Opera downloads automatically: return FALSE
                */
           BOOL IsEnd(OpStringC info) { return FALSE; }
           void Dispose()
           {
             m_winbar->NewWindow("data:text/plain,User-Agent%20overrides%20have%20been%20downloaded");
             delete this;
           }
           private:
             GOGIUI_WindowBar *m_winbar;
         };

         g_PrefsLoadManager->InitLoader(UNI_L(""), new AcceptPrefDownload(&winbar));
       #endif // PREFS_DOWNLOAD

## Supported standards

The downloaded preferences are tagged as XML.

## Implementation and design

The parsing of the downloaded preferences is done with an XML-parser through the xmlutils module API. This should make it easy to switch to another parser if wanted.

Interpretation of tags and attribute names in the xml format are done with two functions, `GetElmType` and `GetAttType`. To extend the xml-format, change these functions and their coresponding enum types. The preferences actually allowed to be downloaded is listed and checked in the funcion `IsAcceptedPref`. Extend this if there is a demand for more.

The parser does not verify the syntax. Preferences and their values are extracted from the source under the assumption that the syntax is correct. If this is not so, the behaviour is undefined. The format is defined in the Preference Download DTD and described below.

The vast majority of functions that require memory to be allocated is using the `OP_STATUS` return value.

## Server side implementation

The server is implemented as a cgi-script. The url of the server has the form

     location/prefs.cgi?host=hostname

Where `hostname` is the name of the specific site.

If the host name is blank, * or if the host argument is omitted, the server script should return preference data for all hosts that we have overridden preferences for.

### Format

The server should return data on this form, if `hostname` is www.starbucks.com:

     <?xml version="1.0" encoding="ISO-8859-1"?>
     <preferences>
      <delete-file value="styles/green.css"/>
      <host name="www.starbucks.com"  clean="1">
       <section name="User Agent">
        <pref name="Spoof UserAgent ID" value="3"/>
       </section>
       <section name="User Prefs">
        <file name="Browser CSS File" value="styles/red.css" from="https://homes.oslo.opera.com/tord/site/red.css"/>
       </section>
      </host>
     </preferences>

Syntax explanation:

     <preferences>

The root element. The preference data is enclosed in this element. It can contain <host>-elements, <section>-elements and <delete-file>-elements.

      <host name="www.starbucks.com"  clean="1">

This element surrounds all preference overrides for a particular host. The name attribute names the site for which the overrides are valid. The element can contain `<section>`-elements. The presence of the `clean`-attribute indicates that old overrides should be removed before new ones are applied. An empty host element with clean on, indicates that this host hasn't got overrides any more.

       <section name="User Agent">

The name attribute of the section element shows the section name of the enclosed `<pref>`- and `<file>`- elements. If the element is placed inside a host element, it contains overrides for that host. If it is located directly inside the root element, it will contain preferences valid for all hosts.

        <pref name="Spoof UserAgent ID" value="3"/>

The pref element contains the actual override for the preference. The `name`-attribute contains the name of the element, and `value` contains the content as it will be set in the `opera.ini` file.

        <file name="Browser CSS File" value="styles/red.css" from="https://homes.oslo.opera.com/tord/site/red.css"/>

The file element works as pref element, but in addition it downloads a file to a location corresponding to the value-attribute. The resulting location of the file, which will be the value of the preference, is Operas `OPFILE_PREFSLOAD_FOLDER,` directory, + the `value`-attribute. If no `value`-attribute is given, The file will be downloaded to the location given by the old preference value. The `from`-attribute contains the url of the file that will be downloaded.

      <delete-file value="styles/green.css"/>

If old overrides are removed, a previously downloaded utility file may be obsolete. This element indicates that it can be removed. The actual path of the file that will be removed is constructed in the same way as in the file element.

Additionally, there is a `clean_all` attribute on the preferences tag itself that causes all non-user defined overrides to be removed before the preference XML is applied. This is useful to avoid getting a very large spoof XML file whenever a large number of sites have previously had spoofs (if there was no `clean_all` attribute each previously spoofed host would have to remain in the spoof XML listed with a `clean` attribute for a very long time to ensure that late upgraders get a good experience). For example, the XML snippet below will remove all previously downloaded preference overrides for all hostnames, and then make sure that the only remaining non-user defined per-host override is that when visiting www.cnn.com Opera will send user agent ID "2" (which is Mozilla):

     <?xml version="1.0" encoding="ISO-8859-1"?>
     <preferences clean_all="1">
      <host name="www.cnn.com">
       <section name="User Agent">
        <pref name="Spoof UserAgent ID" value="2"/>
       </section>
      </host>
     </preferences>

### Server location

Opera8 and later uses `http://xml.opera.com/spoof/` as preference server.

## Manual testing

When testing this module manually you might want to setup a small local webserver and then make sure that Opera uses this webserver for retrieving spoof updates instead of the standard xml.opera.com server. To do that you can add the following two tweaks into `./platforms/core/tweaks.h` and then recompile:

    #undef TWEAK_GOGI_SITEPATCH_SPOOF_URL
    #define TWEAK_GOGI_SITEPATCH_SPOOF_URL          YES
    #define GOGI_SITEPATCH_SPOOF_URL "http://localhost:8000/"

    #undef TWEAK_GOGI_SITEPATCH_DELAY
    #define TWEAK_GOGI_SITEPATCH_DELAY              YES
    #define GOGI_SITEPATCH_DELAY                    1

As a local lightweight webserver, you can use the Python script in `./modules/prefsloader/tools/httpd.py`