<commit_before>//
//  AntiTrackingModule.swift
//  Client
//
//  Created by <NAME> on 8/12/16.
//  Copyright © 2016 Mozilla. All rights reserved.
//

import Foundation
import JavaScriptCore
import Crashlytics


class AntiTrackingModule: NSObject {
    
    //MARK: Constants
    private let context = JSContext()
    private let antiTrackingDirectory = "Extension/build/mobile/search/v8"
    private let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! as String
    private let fileManager = NSFileManager.defaultManager()
    private let dispatchQueue = dispatch_queue_create("com.cliqz.AntiTracking", DISPATCH_QUEUE_CONCURRENT)
    
    private let adBlockABTestPrefName = "cliqz-adb-abtest"
    private let adBlockPrefName = "cliqz-adb"
    
    private let telemetryWhiteList = ["attrack.FP", "attrack.tp_events"]
    
    // MARK: - Local variables
    var privateMode = false
    var timerCounter = 0
    var timers = [Int: NSTimer]()
    
    //MARK: - Singltone
    static let sharedInstance = AntiTrackingModule()
    
    override init() {
        super.init()
    }
    
    //MARK: - Public APIs
    func initModule() {
        // Register interceptor url protocol
        NSURLProtocol.registerClass(InterceptorURLProtocol)
        
        dispatch_async(dispatchQueue) {
            self.configureExceptionHandler()
            self.loadModule()
        }
    }
    
    func getModifiedRequest(originalRequest: NSURLRequest) -> NSMutableURLRequest? {
        
//        let modifiedRequest = originalRequest.mutableCopy() as! NSMutableURLRequest
        let modifiedRequest = AntiTrackingModule.cloneRequest(originalRequest)
        
        let requestInfo = getRequestInfo(originalRequest)
        if let blockResponse = getBlockResponseForRequest(requestInfo) where blockResponse.count > 0 {
            
            if let block = blockResponse["cancel"] as? Bool where block == true {
//                print("[Anti-Tracking] request blocked")
                return nil
            }
            
            if let redirectUrl = blockResponse["redirectUrl"] as? String {
                modifiedRequest.URL = NSURL(string: redirectUrl)!
                
                if let tabId = requestInfo["tabId"] as? Int,
                    let webView = WebViewToUAMapper.idToWebView(tabId) {
                    webView.incrementBadRequestsCount()
                }
//                print("[Anti-Tracking] request redirected")
            }
            if let requestHeaders = blockResponse["requestHeaders"] as? [[String: String]] {
                
                for requestHeader in requestHeaders {
                    modifiedRequest.setValue(requestHeader["value"], forHTTPHeaderField: requestHeader["name"]!)
                }
            }
        }
        return modifiedRequest
    }
    
    func setAdblockEnabled(value: Bool) {
        dispatch_async(dispatchQueue) {
            self.context.evaluateScript("CliqzUtils.setPref(\"\(self.adBlockABTestPrefName)\", \(value));")

            self.context.evaluateScript("CliqzUtils.setPref(\"\(self.adBlockPrefName)\", \(value ? 1 : 0));")
        }
    }
	
	func getTrackersCount(webViewId: Int) -> Int {
		if let webView = WebViewToUAMapper.idToWebView(webViewId) {
			return webView.badRequests
		}
		return 0
	}

    func getAntiTrackingStatistics(webViewId: Int) -> [(String, Int)] {
        var antiTrackingStatistics = [(String, Int)]()
        
        let tabBlockInfo = self.context.evaluateScript("System.get('antitracking/attrack').default.getTabBlockingInfo(\(webViewId));").toDictionary()
        
        if let companies = tabBlockInfo["companies"] as? [String: [String]],
            let allTrackers = tabBlockInfo["trackers"] as? [String: AnyObject] {
            
            for (company, trackers) in companies {
                let badRequestsCount = getCompanyBadRequestsCount(trackers, allTrackers:allTrackers)
				if badRequestsCount > 0 {
					antiTrackingStatistics.append((company, badRequestsCount))
				}
            }
        }
        
        return antiTrackingStatistics
    }

    //MARK: - Private Helpers
    private func getCompanyBadRequestsCount(trackers: [String], allTrackers: [String: AnyObject]) -> Int {
        var badRequestsCount = 0
        for tracker in trackers {
            if let trackerStatistics = allTrackers[tracker] as? [String: Int],
                let badRequests = trackerStatistics["bad_qs"] {
                badRequestsCount += badRequests
            }
        }
        
        return badRequestsCount
    }

    private class func cloneRequest(request: NSURLRequest) -> NSMutableURLRequest {
        // Reportedly not safe to use built-in cloning methods: http://openradar.appspot.com/11596316
        let newRequest = NSMutableURLRequest(URL: request.URL!, cachePolicy: request.cachePolicy, timeoutInterval: request.timeoutInterval)
        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields
        if let m = request.HTTPMethod {
            newRequest.HTTPMethod = m
        }
        if let b = request.HTTPBodyStream {
            newRequest.HTTPBodyStream = b
        }
        if let b = request.HTTPBody {
            newRequest.HTTPBody = b
        }
        newRequest.HTTPShouldUsePipelining = request.HTTPShouldUsePipelining
        newRequest.mainDocumentURL = request.mainDocumentURL<commit_msg>Sorted trackers on count desc, name alphabetically ignoring case<commit_after>//
//  AntiTrackingModule.swift
//  Client
//
//  Created by <NAME> on 8/12/16.
//  Copyright © 2016 Mozilla. All rights reserved.
//

import Foundation
import JavaScriptCore
import Crashlytics


class AntiTrackingModule: NSObject {
    
    //MARK: Constants
    private let context = JSContext()
    private let antiTrackingDirectory = "Extension/build/mobile/search/v8"
    private let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! as String
    private let fileManager = NSFileManager.defaultManager()
    private let dispatchQueue = dispatch_queue_create("com.cliqz.AntiTracking", DISPATCH_QUEUE_CONCURRENT)
    
    private let adBlockABTestPrefName = "cliqz-adb-abtest"
    private let adBlockPrefName = "cliqz-adb"
    
    private let telemetryWhiteList = ["attrack.FP", "attrack.tp_events"]
    
    // MARK: - Local variables
    var privateMode = false
    var timerCounter = 0
    var timers = [Int: NSTimer]()
    
    //MARK: - Singltone
    static let sharedInstance = AntiTrackingModule()
    
    override init() {
        super.init()
    }
    
    //MARK: - Public APIs
    func initModule() {
        // Register interceptor url protocol
        NSURLProtocol.registerClass(InterceptorURLProtocol)
        
        dispatch_async(dispatchQueue) {
            self.configureExceptionHandler()
            self.loadModule()
        }
    }
    
    func getModifiedRequest(originalRequest: NSURLRequest) -> NSMutableURLRequest? {
        
//        let modifiedRequest = originalRequest.mutableCopy() as! NSMutableURLRequest
        let modifiedRequest = AntiTrackingModule.cloneRequest(originalRequest)
        
        let requestInfo = getRequestInfo(originalRequest)
        if let blockResponse = getBlockResponseForRequest(requestInfo) where blockResponse.count > 0 {
            
            if let block = blockResponse["cancel"] as? Bool where block == true {
//                print("[Anti-Tracking] request blocked")
                return nil
            }
            
            if let redirectUrl = blockResponse["redirectUrl"] as? String {
                modifiedRequest.URL = NSURL(string: redirectUrl)!
                
                if let tabId = requestInfo["tabId"] as? Int,
                    let webView = WebViewToUAMapper.idToWebView(tabId) {
                    webView.incrementBadRequestsCount()
                }
//                print("[Anti-Tracking] request redirected")
            }
            if let requestHeaders = blockResponse["requestHeaders"] as? [[String: String]] {
                
                for requestHeader in requestHeaders {
                    modifiedRequest.setValue(requestHeader["value"], forHTTPHeaderField: requestHeader["name"]!)
                }
            }
        }
        return modifiedRequest
    }
    
    func setAdblockEnabled(value: Bool) {
        dispatch_async(dispatchQueue) {
            self.context.evaluateScript("CliqzUtils.setPref(\"\(self.adBlockABTestPrefName)\", \(value));")

            self.context.evaluateScript("CliqzUtils.setPref(\"\(self.adBlockPrefName)\", \(value ? 1 : 0));")
        }
    }
	
	func getTrackersCount(webViewId: Int) -> Int {
		if let webView = WebViewToUAMapper.idToWebView(webViewId) {
			return webView.badRequests
		}
		return 0
	}

    func getAntiTrackingStatistics(webViewId: Int) -> [(String, Int)] {
        var antiTrackingStatistics = [(String, Int)]()
        
        let tabBlockInfo = self.context.evaluateScript("System.get('antitracking/attrack').default.getTabBlockingInfo(\(webViewId));").toDictionary()
        
        if let companies = tabBlockInfo["companies"] as? [String: [String]],
            let allTrackers = tabBlockInfo["trackers"] as? [String: AnyObject] {
            
            for (company, trackers) in companies {
                let badRequestsCount = getCompanyBadRequestsCount(trackers, allTrackers:allTrackers)
				if badRequestsCount > 0 {
					antiTrackingStatistics.append((company, badRequestsCount))
				}
            }
        }
        return antiTrackingStatistics.sort { $0.1 == $1.1 ? $0.0.lowercaseString < $1.0.lowercaseString : $0.1 > $1.1 }
    }

    //MARK: - Private Helpers
    private func getCompanyBadRequestsCount(trackers: [String], allTrackers: [String: AnyObject]) -> Int {
        var badRequestsCount = 0
        for tracker in trackers {
            if let trackerStatistics = allTrackers[tracker] as? [String: Int],
                let badRequests = trackerStatistics["bad_qs"] {
                badRequestsCount += badRequests
            }
        }
        
        return badRequestsCount
    }

    private class func cloneRequest(request: NSURLRequest) -> NSMutableURLRequest {
        // Reportedly not safe to use built-in cloning methods: http://openradar.appspot.com/11596316
        let newRequest = NSMutableURLRequest(URL: request.URL!, cachePolicy: request.cachePolicy, timeoutInterval: request.timeoutInterval)
        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields
        if let m = request.HTTPMethod {
            newRequest.HTTPMethod = m
        }
        if let b = request.HTTPBodyStream {
            newRequest.HTTPBodyStream = b
        }
        if let b = request.HTTPBody {
            newRequest.HTTPBody = b
        }
        newRequest.HTTPShouldUsePipelining = request.HTTPShouldUsePipelining
        newRequest.mainDocumentURL = request.mainDocumentURL
{'fraction_non_alphanumeric': 0.09208371246587807, 'fraction_numerical': 0.005641492265696087, 'mean_word_length': 2.394379246448425, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5010542', 'n_tokens_mistral': 3061, 'n_tokens_neox': 2920, 'n_words': 643}