<commit_before>/* eslint standard/no-callback-literal: 0 */
/** Proxy management.
 *
 * @module gui/js/proxy
 */

const ElectronProxyAgent = require('electron-proxy-agent')
const url = require('url')
const http = require('http')
const https = require('https')
const process = require('process')
const yargs = require('yargs')

const log = require('../../core/app').logger({
  component: 'GUI:proxy'
})

const config = (argv = process.argv) => {
  const config = yargs
    .env('COZY_DRIVE')
    .conflicts('proxy-script', 'proxy-rules')
    .describe('proxy-script', 'The URL associated with the PAC file.')
    .describe('proxy-rules', 'Rules indicating which proxies to use.')
    .describe(
      'proxy-bypassrules',
      'Rules indicating which URLs should bypass the proxy settings. ' +
        'See https://github.com/electron/electron/blob/master/docs/api/session.md#sessetproxyconfig-callback'
    )
    .default('proxy-ntlm-domains', '*')
    .describe(
      'proxy-ntlm-domains',
      'A comma-separated list of servers for which integrated authentication is enabled. ' +
        'Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate authentication.'
    )
    .describe('login-by-realm', 'comma-separated list of realm:user:password')
    .help('help')
    .parse(argv)

  log.debug({ config }, 'argv')
  return config
}

const formatCertificate = certif =>
  `Certificate(${certif.issuerName} ${certif.subjectName})`

const setup = (app, config, session, userAgent, doneSetup) => {
  const loginByRealm = {}
  if (config['login-by-realm']) {
    config['login-by-realm'].split(',').forEach(lbr => {
      const [realm, username, ...password] = lbr.split(':')
      loginByRealm[realm] = [username, password.join(':')]
    })
  }

  if (config['proxy-ntlm-domains']) {
    session.defaultSession.allowNTLMCredentialsForDomains(
      config['proxy-ntlm-domains']
    )
  }

  session.defaultSession.setCertificateVerifyProc((request, callback) => {
    const { hostname, certificate, verificationResult, errorCode } = request
    if (verificationResult < 0) {
      log.warn(
        {
          hostname,
          certificate: formatCertificate(certificate),
          verificationResult,
          errorCode
        },
        'Certificate Verification Error'
      )
    }
    callback(-3) // use chrome validation
  })

  app.on(
    'select-client-certificate',
    (event, webContents, url, list, callback) => {
      log.debug({ url }, 'select-client-certificate')
      callback()
    }
  )

  app.on(
    'certificate-error',
    (event, webContents, url, error, certificate, callback) => {
      log.warn(
        { url, error, certificate: formatCertificate(certificate) },
        'App Certificate Error'
      )
      callback(false)
    }
  )

  app.on('login', (event, webContents, request, authInfo, callback) => {
    log.debug({ request: request.method + ' ' + request.url }, 'Login event')
    const auth = loginByRealm[authInfo.realm]
    if (auth) {
      event.preventDefault()
      callback(...auth)
    } else {
      callback()
    }
  })

  // XXX even if we swicth from electron-fetch, keep the custom user-agent
  const originalFetch = global.fetch
  const electronFetch = require('electron-fetch')
  global.fetch = (url, opts = {}) => {
    opts.session = session.defaultSession
    opts.headers = opts.headers || {}
    opts.headers['User-Agent'] = userAgent
    return electronFetch(url, opts)
  }
  http.Agent.globalAgent = http.globalAgent = https.globalAgent = new ElectronProxyAgent(
    session.defaultSession
  )
  const originalHttpRequest = http.request
  http.request = function(options, cb) {
    options.agent = options.agent || http.globalAgent
    options.headers = options.headers || {}
    if (options.hostname) options.headers.host = options.hostname
    options.headers['User-Agent'] = userAgent
    return originalHttpRequest.call(http, options, cb)
  }
  const originalHttpsRequest = https.request
  https.request = function(options, cb) {
    if (typeof options === 'string') {
      options = new url.URL(options)
    } else {
      options = Object.assign({}, options)
    }
    options.agent = options.agent || https.globalAgent
    return originalHttpsRequest.call(https, options, cb)<commit_msg>gui/proxy: Add request options for HTTP and HTTPS<commit_after>/* eslint standard/no-callback-literal: 0 */
/** Proxy management.
 *
 * @module gui/js/proxy
 */

const ElectronProxyAgent = require('electron-proxy-agent')
const url = require('url')
const http = require('http')
const https = require('https')
const process = require('process')
const yargs = require('yargs')

const log = require('../../core/app').logger({
  component: 'GUI:proxy'
})

const config = (argv = process.argv) => {
  const config = yargs
    .env('COZY_DRIVE')
    .conflicts('proxy-script', 'proxy-rules')
    .describe('proxy-script', 'The URL associated with the PAC file.')
    .describe('proxy-rules', 'Rules indicating which proxies to use.')
    .describe(
      'proxy-bypassrules',
      'Rules indicating which URLs should bypass the proxy settings. ' +
        'See https://github.com/electron/electron/blob/master/docs/api/session.md#sessetproxyconfig-callback'
    )
    .default('proxy-ntlm-domains', '*')
    .describe(
      'proxy-ntlm-domains',
      'A comma-separated list of servers for which integrated authentication is enabled. ' +
        'Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate authentication.'
    )
    .describe('login-by-realm', 'comma-separated list of realm:user:password')
    .help('help')
    .parse(argv)

  log.debug({ config }, 'argv')
  return config
}

const formatCertificate = certif =>
  `Certificate(${certif.issuerName} ${certif.subjectName})`

const setup = (app, config, session, userAgent, doneSetup) => {
  const loginByRealm = {}
  if (config['login-by-realm']) {
    config['login-by-realm'].split(',').forEach(lbr => {
      const [realm, username, ...password] = lbr.split(':')
      loginByRealm[realm] = [username, password.join(':')]
    })
  }

  if (config['proxy-ntlm-domains']) {
    session.defaultSession.allowNTLMCredentialsForDomains(
      config['proxy-ntlm-domains']
    )
  }

  session.defaultSession.setCertificateVerifyProc((request, callback) => {
    const { hostname, certificate, verificationResult, errorCode } = request
    if (verificationResult < 0) {
      log.warn(
        {
          hostname,
          certificate: formatCertificate(certificate),
          verificationResult,
          errorCode
        },
        'Certificate Verification Error'
      )
    }
    callback(-3) // use chrome validation
  })

  app.on(
    'select-client-certificate',
    (event, webContents, url, list, callback) => {
      log.debug({ url }, 'select-client-certificate')
      callback()
    }
  )

  app.on(
    'certificate-error',
    (event, webContents, url, error, certificate, callback) => {
      log.warn(
        { url, error, certificate: formatCertificate(certificate) },
        'App Certificate Error'
      )
      callback(false)
    }
  )

  app.on('login', (event, webContents, request, authInfo, callback) => {
    log.debug({ request: request.method + ' ' + request.url }, 'Login event')
    const auth = loginByRealm[authInfo.realm]
    if (auth) {
      event.preventDefault()
      callback(...auth)
    } else {
      callback()
    }
  })

  // XXX even if we swicth from electron-fetch, keep the custom user-agent
  const originalFetch = global.fetch
  const electronFetch = require('electron-fetch')
  global.fetch = (url, opts = {}) => {
    opts.session = session.defaultSession
    opts.headers = opts.headers || {}
    opts.headers['User-Agent'] = userAgent
    return electronFetch(url, opts)
  }
  http.Agent.globalAgent = http.globalAgent = https.globalAgent = new ElectronProxyAgent(
    session.defaultSession
  )
  const parseRequestOptions = options => {
    if (typeof options === 'string') {
      options = new url.URL(options)
    } else {
      options = Object.assign({}, options)
    }
    options.agent = options.agent || http.globalAgent
    options.headers = options.headers || {}
    if (options.hostname) options.headers.host = options.hostname
    options.headers['User-Agent'] = userAgent
    return options
  }
  const originalHttpRequest = http.request
  http.request = function(options, cb) {
    return originalHttpRequest.call(http, parseRequestOptions(options), cb)
  }
  const originalHttpsRequest = https.request
  https.request = function(options, cb) {
    return originalHttpsRequest.call(https, parseRequestOptions(options), cb)
{'fraction_non_alphanumeric': 0.13841189351158234, 'fraction_numerical': 0.0006914832315316353, 'mean_word_length': 3.2003872216844145, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 22, 'https://': 2, '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': '4767477', 'n_tokens_mistral': 2619, 'n_tokens_neox': 2561, 'n_words': 588}