Skip to content
Snippets Groups Projects
Forked from Thomas Rientjes / decentraleyes
285 commits behind, 31 commits ahead of the upstream repository.
interceptor.js 3.81 KiB
/**
 * Interceptor
 * Belongs to Decentraleyes.
 *
 * @author      Thomas Rientjes
 * @since       2014-05-30
 * @license     MPL 2.0
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';

/**
 * Imports
 */

var {Class} = require('sdk/core/heritage');
var {Unknown} = require('sdk/platform/xpcom');
var {Cc, Ci, Cr} = require('chrome');

var main = require('./main');

/**
 * Gets and sets add-on specific preferences.
 * @var {object} simplePreferences
 */
var simplePreferences = require('sdk/simple-prefs');

/**
 * Retains data across application restarts.
 * @var {object} simpleStorage
 */
var simpleStorage = require('sdk/simple-storage');

//noinspection JSUnresolvedFunction
var observerService = Cc['@mozilla.org/observer-service;1']
    .getService(Ci.nsIObserverService);

var requestAnalyzer = require('./request-analyzer');
var dataHandler = require('./data-handler');

/**
 * Variables
 */

var preferences = simplePreferences.prefs;
var storage = simpleStorage.storage;

/**
 * Interceptor Class
 */

var Interceptor = new Class({

    'extends': Unknown,
    'interfaces': ['nsIObserver'],
    'topic': 'http-on-modify-request',

    'register': function () {
        observerService.addObserver(this, this.topic, false);
    },

    'unregister': function () {
        observerService.removeObserver(this, this.topic);
    },

    /**
     * Called whenever an HTTP request is made.
     * @param httpChannel
     */
    'observe': function (httpChannel) {

        var validCandidate, target, characterSet, redirectionURI, initiatorDomain;

        // Enable runtime discovery.
        httpChannel.QueryInterface(Ci.nsIHttpChannel);

        // Determine the validity of the candidate.
        validCandidate = requestAnalyzer.isValidCandidate(httpChannel);

        if (!validCandidate) {
            return;
        }

        // Remove sensitive headers from the request.
        httpChannel.setRequestHeader('Referer', null, false);
        httpChannel.setRequestHeader('Origin', null, false);
        httpChannel.setRequestHeader('Cookie', null, false);

        // Convert the original request URI to a local target.
        target = requestAnalyzer.getLocalTarget(httpChannel.URI.host, httpChannel.URI.path);

        if (!target) {
            return this.handleMissingCandidate(httpChannel);
        }

        characterSet = httpChannel.URI.originCharset;

        // Fetch local data and create a redirection URI.
        try {
            redirectionURI = dataHandler.getRedirectionURI(target.path, characterSet, target.type);
        } catch (exception) {
            return this.handleMissingCandidate(httpChannel);
        }

        // Fix for reported edge-case issues with specific websites.
        initiatorDomain =
            httpChannel.loadInfo && httpChannel.loadInfo.loadingDocument &&
            httpChannel.loadInfo.loadingDocument.domain ||
            httpChannel.referrer && httpChannel.referrer.host;

        if (storage.taintedDomains[initiatorDomain] || (/yandex\./).test(initiatorDomain)) {
            return this.handleMissingCandidate(httpChannel);
        }

        // Redirect the HTTP channel to the the local destination.
        httpChannel.redirectTo(redirectionURI);

        //noinspection JSUnresolvedVariable
        preferences.amountInjected++;
        main.broadcastInjection();
    },

    /**
     * Called when a valid candidate cannot be injected.
     * @param httpChannel
     */
    'handleMissingCandidate': function (httpChannel) {

        //noinspection JSUnresolvedVariable
        if (preferences.blockMissing) {
            httpChannel.cancel(Cr.NS_ERROR_NOT_AVAILABLE);
        }
    }
});

/**
 * Exports
 */

module.exports = Interceptor;