text:
import * as OS from 'os'
import * as URL from 'url'
import { Account } from '../models/account'

import {
  request,
  parsedResponse,
  HTTPMethod,
  APIError,
  urlWithQueryString,
} from './http'
import { AuthenticationMode } from './2fa'
import { uuid } from './uuid'
import username from 'username'
import { GitProtocol } from './remote-parsing'
import { Emitter } from 'event-kit'
import JSZip from 'jszip'

const envEndpoint = process.env['DESKTOP_GITHUB_DOTCOM_API_ENDPOINT']
const envHTMLURL = process.env['DESKTOP_GITHUB_DOTCOM_HTML_URL']
const envAdditionalCookies =
  process.env['DESKTOP_GITHUB_DOTCOM_ADDITIONAL_COOKIES']

if (envAdditionalCookies !== undefined) {
  document.cookie += '; ' + envAdditionalCookies
}

/**
 * Optional set of configurable settings for the fetchAll method
 */
interface IFetchAllOptions<T> {
  /**
   * The number of results to ask for on each page when making
   * requests to paged API endpoints.
   */
  perPage?: number

  /**
   * An optional predicate which determines whether or not to
   * continue loading results from the API. This can be used
   * to put a limit on the number of results to return from
   * a paged API resource.
   *
   * As an example, to stop loading results after 500 results:
   *
   * `(results) => results.length < 500`
   *
   * @param results  All results retrieved thus far
   */
  continue?: (results: ReadonlyArray<T>) => boolean

  /**
   * Calculate the next page path given the response.
   *
   * Optional, see `getNextPagePathFromLink` for the default
   * implementation.
   */
  getNextPagePath?: (response: Response) => string | null

  /**
   * Whether or not to silently suppress request errors and
   * return the results retrieved thus far. If this field is
   * `true` the fetchAll method will suppress errors (this is
   * also the default behavior if no value is provided for
   * this field). Setting this field to false will cause the
   * fetchAll method to throw if it encounters an API error
   * on any page.
   */
  suppressErrors?: boolean
}

const ClientID = process.env.TEST_ENV ? '' : __OAUTH_CLIENT_ID__
const ClientSecret = process.env.TEST_ENV ? '' : __OAUTH_SECRET__

if (!ClientID || !ClientID.length || !ClientSecret || !ClientSecret.length) {
  log.warn(
    `DESKTOP_OAUTH_CLIENT_ID and/or DESKTOP_OAUTH_CLIENT_SECRET is undefined. You won't be able to authenticate new users.`
  )
}

type GitHubAccountType = 'User' | 'Organization'

/** The OAuth scopes we want to request from GitHub.com. */
const DotComOAuthScopes = ['repo', 'user', 'workflow']

/**
 * The OAuth scopes we want to request from GitHub
 * Enterprise.
 */
const EnterpriseOAuthScopes = ['repo', 'user']

enum HttpStatusCode {
  NotModified = 304,
  NotFound = 404,
}

/** The note URL used for authorizations the app creates. */
const NoteURL = 'https://desktop.github.com/'

/**
 * Information about a repository as returned by the GitHub API.
 */
export interface IAPIRepository {
  readonly clone_url: string
  readonly ssh_url: string
  readonly html_url: string
  readonly name: string
  readonly owner: IAPIIdentity
  readonly private: boolean
  readonly fork: boolean
  readonly default_branch: string
  readonly pushed_at: string
  readonly has_issues: boolean
  readonly archived: boolean
}

/** Information needed to clone a repository. */
export interface IAPIRepositoryCloneInfo {
  /** Canonical clone URL of the repository. */
  readonly url: string

  /**
   * Default branch of the repository, if any. This is usually either retrieved
   * from the API for GitHub repositories, or undefined for other repositories.
   */
  readonly defaultBranch?: string
}

export interface IAPIFullRepository extends IAPIRepository {
  /**
   * The parent repository of a fork.
   *
   * HACK: BEWARE: This is defined as `parent: IAPIRepository | undefined`
   * rather than `parent?: ...` even though the parent property is actually
   * optional in the API response. So we're lying a bit to the type system
   * here saying that this will be present but the only time the difference
   * between omission and explicit undefined matters is when using constructs
   * like `x in y` or `y.hasOwnProperty('x')` which we do very rarely.
   *
   * Without at least one non-optional type in this interface TypeScript will
   * happily let us pass an IAPIRepository in place of an IAPIFullRepository.
   */
  readonly parent: IAPIRepository | undefined

  /**
   * The high-level permissions that the currently authenticated
   * user enjoys for the repository. Undefined if the API call
   * was made without an authenticated user or if the repository
   * isn't the primarily requested one (i.e. if this is the parent
   * repository of the requested repository)
   *
   * The permissions hash will also be omitted when the repository
   * information is embedded within another object such as a pull
   * request (base.repo or head.repo).
   *
   * In other words, the only time when the permissions property
   * will be present is when explicitly fetching the repository
   * through the `/repos/user/name` endpoint or similar.
   */
  readonly permissions?: IAPIRepositoryPermissions
}

/*
 * Information about how the user is permitted to interact with a repository.
 */
export interface IAPIRepositoryPermissions {
  readonly admin: boolean
  /* aka 'write' */
  readonly push: boolean
  /* aka 'read' */
  readonly pull: boolean
}

/**
 * Information about a commit as returned by the GitHub API.
 */
export interface IAPICommit {
  readonly sha: string
  readonly author: IAPIIdentity | {} | null
}

/**
 * Entity returned by the `/user/orgs` endpoint.
 *
 * Because this is specific to one endpoint it omits the `type` member from
 * `IAPIIdentity` that callers might expect.
 */
export interface IAPIOrganization {
  readonly id: number
  readonly url: string
  readonly login: string
  readonly avatar_url: string
}

/**
 * Minimum subset of an identity returned by the GitHub API
 */
export interface IAPIIdentity {
  readonly id: number
  readonly url: string
  readonly login: string
  readonly avatar_url: string
  readonly type: GitHubAccountType
}

/**
 * Complete identity details returned in some situations by the GitHub API.
 *
 * If you are not sure what is returned as part of an API response, you should
 * use `IAPIIdentity` as that contains the known subset of an identity and does
 * not cover scenarios where privacy settings of a user control what information
 * is returned.
 */
interface IAPIFullIdentity {
  readonly id: number
  readonly url: string
  readonly login: string
  readonly avatar_url: string

  /**
   * The user's real name or null if the user hasn't provided
   * a real name for their public profile.
   */
  readonly name: string | null

  /**
   * The email address for this user or null if the user has not
   * specified a public email address in their profile.
   */
  readonly email: string | null
  readonly type: GitHubAccountType
}

/** The users we get from the mentionables endpoint. */
export interface IAPIMentionableUser {
  /**
   * A url to an avatar image chosen by the user
   */
  readonly avatar_url: string

  /**
   * The user's attributable email address or null if the
   * user doesn't have an email address that they can be
   * attributed by
   */
  readonly email: string | null

  /**
   * The username or "handle" of the user
   */
  readonly login: string

  /**
   * The user's real name (or at least the name that the user
   * has configured to be shown) or null if the user hasn't provided
   * a real name for their public profile.
   */
  readonly name: string | null
}

/**
 * Error thrown by `fetchUpdatedPullRequests` when receiving more results than
 * what the `maxResults` parameter allows for.
 */
export class MaxResultsError extends Error {}

/**
 * `null` can be returned by the API for legacy reasons. A non-null value is
 * set for the primary email address currently, but in the future visibility
 * may be defined for each email address.
 */
export type EmailVisibility = 'public' | 'private' | null

/**
 * Information about a user's email as returned by the GitHub API.
 */
export interface IAPIEmail {
  readonly email: string
  readonly verified: boolean
  readonly primary: boolean
  readonly visibility: EmailVisibility
}

/** Information about an issue as returned by the GitHub API. */
export interface IAPIIssue {
  readonly number: number
  readonly title: string
  readonly state: 'open' | 'closed'
  readonly updated_at: string
}

/** The combined state of a ref. */
export type APIRefState = 'failure' | 'pending' | 'success' | 'error'

/** The overall status of a check run */
export enum APICheckStatus {
  Queued = 'queued',
  InProgress = 'in_progress',
  Completed = 'completed',
}

/** The conclusion of a completed check run */
export enum APICheckConclusion {
  ActionRequired = 'action_required',
  Canceled = 'cancelled',
  TimedOut = 'timed_out',
  Failure = 'failure',
  Neutral = 'neutral',
  Success = 'success',
  Skipped = 'skipped',
  Stale = 'stale',
}

/**
 * The API response for a combined view of a commit
 * status for a given ref
 */
export interface IAPIRefStatusItem {
  readonly state: APIRefState
  readonly target_url: string | null
  readonly description: string
  readonly context: string
  readonly id: number
}

/** The API response to a ref status request. */
export interface IAPIRefStatus {
  readonly state: APIRefState
  readonly total_count: number
  readonly statuses: ReadonlyArray<IAPIRefStatusItem>
}

export interface IAPIRefCheckRun {
  readonly id: number
  readonly url: string
  readonly status: APICheckStatus
  readonly conclusion: APICheckConclusion | null
  readonly name: string
  readonly check_suite: IAPIRefCheckRunCheckSuite
  readonly app: IAPIRefCheckRunApp
  readonly completed_at: string
  readonly started_at: string
  readonly html_url: string
  readonly pull_requests: ReadonlyArray<IAPIPullRequest>
}

// NB. Only partially mapped
export interface IAPIRefCheckRunApp {
  readonly name: string
}

// NB. Only partially mapped
export interface IAPIRefCheckRunOutput {
  readonly title: string | null
  readonly summary: string | null
  readonly text: string | null
}

export interface IAPIRefCheckRunCheckSuite {
  readonly id: number
}

export interface IAPICheckSuite {
  readonly id: number
  readonly rerequestable: boolean
  readonly runs_rerequestable: boolean
  readonly status: APICheckStatus
  readonly created_at: string
}

export interface IAPIRefCheckRuns {
  readonly total_count: number
  readonly check_runs: IAPIRefCheckRun[]
}

interface IAPIWorkflowRuns {
  readonly total_count: number
  readonly workflow_runs: ReadonlyArray<IAPIWorkflowRun>
}
// NB. Only partially mapped
export interface IAPIWorkflowRun {
  readonly id: number
  /**
   * The workflow_id is the id of the workflow not the individual run.
   **/
  readonly workflow_id: number
  readonly cancel_url: string
  readonly created_at: string
  readonly logs_url: string
  readonly name: string
  readonly rerun_url: string
  readonly check_suite_id: number
  readonly event: string
}

export interface IAPIWorkflowJobs {
  readonly total_count: number
  readonly jobs: IAPIWorkflowJob[]
}

// NB. Only partially mapped
export interface IAPIWorkflowJob {
  readonly id: number
  readonly name: string
  readonly status: APICheckStatus
  readonly conclusion: APICheckConclusion | null
  readonly completed_at: string
  readonly started_at: string
  readonly steps: ReadonlyArray<IAPIWorkflowJobStep>
  readonly html_url: string
}

export interface IAPIWorkflowJobStep {
  readonly name: string
  readonly number: number
  readonly status: APICheckStatus
  readonly conclusion: APICheckConclusion | null
  readonly completed_at: string
  readonly started_at: string
  readonly log: string
}

/** Protected branch information returned by the GitHub API */
export interface IAPIPushControl {
  /**
   * What status checks are required before merging?
   *
   * Empty array if user is admin and branch is not admin-enforced
   */
  required_status_checks: Array<string>

  /**
   * How many reviews are required before merging?
   *
   * 0 if user is admin and branch is not admin-enforced
   */
  required_approving_review_count: number

  /**
   * Is user permitted?
   *
   * Always `true` for admins.
   * `true` if `Restrict who can push` is not enabled.
   * `true` if `Restrict who can push` is enabled and user is in list.
   * `false` if `Restrict who can push` is enabled and user is not in list.
   */
  allow_actor: boolean

  /**
   * Currently unused properties
   */
  pattern: string | null
  required_signatures: boolean
  required_linear_history: boolean
  allow_deletions: boolean
  allow_force_pushes: boolean
}

/** Branch information returned by the GitHub API */
export interface IAPIBranch {
  /**
   * The name of the branch stored on the remote.
   *
   * NOTE: this is NOT a fully-qualified ref (i.e. `refs/heads/main`)
   */
  readonly name: string
  /**
   * Branch protection settings:
   *
   *  - `true` indicates that the branch is protected in some way
   *  - `false` indicates no branch protection set
   */
  readonly protected: boolean
}

interface IAPIPullRequestRef {
  readonly ref: string
  readonly sha: string

  /**
   * The repository in which this ref lives. It could be null if the repository
   * has been deleted since the PR was opened.
   */
  readonly repo: IAPIRepository | null
}

/** Information about a pull request as returned by the GitHub API. */
export interface IAPIPullRequest {
  readonly number: number
  readonly title: string
  readonly created_at: string
  readonly updated_at: string
  readonly user: IAPIIdentity
  readonly head: IAPIPullRequestRef
  readonly base: IAPIPullRequestRef
  readonly state: 'open' | 'closed'
  readonly draft?: boolean
}

/** The metadata about a GitHub server. */
export interface IServerMetadata {
  /**
   * Does the server support password-based authentication? If not, the user
   * must go through the OAuth flow to authenticate.
   */
  readonly verifiable_password_authentication: boolean
}

/** The server response when handling the OAuth callback (with code) to obtain an access token */
interface IAPIAccessToken {
  readonly access_token: string
  readonly scope: string
  readonly token_type: string
}

/** The partial server response when creating a new authorization on behalf of a user */
interface IAPIAuthorization {
  readonly token: string
}

/** The response we receive from fetching mentionables. */
interface IAPIMentionablesResponse {
  readonly etag: string | undefined
  readonly users: ReadonlyArray<IAPIMentionableUser>
}

/**
 * Parses the Link header from GitHub and returns the 'next' path
 * if one is present.
 *
 * If no link rel next header is found this method returns null.
 */
function getNextPagePathFromLink(response: Response): string | null {
  const linkHeader = response.headers.get('Link')

  if (!linkHeader) {
    return null
  }

  for (const part of linkHeader.split(',')) {
    // https://github.com/philschatz/octokat.js/blob/5658abe442e8bf405cfda1c72629526a37554613/src/plugins/pagination.js#L17
    const match = part.match(/<([^>]+)>; rel="([^"]+)"/)

    if (match && match[2] === 'next') {
      const nextURL = URL.parse(match[1])
      return nextURL.path || null
    }
  }

  return null
}

/**
 * Parses the 'next' Link header from GitHub using
 * `getNextPagePathFromLink`. Unlike `getNextPagePathFromLink`
 * this method will attempt to double the page size when
 * the current page index and the page size allows for it
 * leading to a ramp up in page size.
 *
 * This might sound confusing, and it is, but the primary use
 * case for this is when retrieving updated PRs. By specifying
 * an initial page size of, for example, 10 this method will
 * increase the page size to 20 once the second page has been
 * loaded. See the table below for an example. The ramp-up
 * will stop at a page size of 100 since that's the maximum
 * that the GitHub API supports.
 *
 * ```
 * |-----------|------|-----------|-----------------|
 * | Request # | Page | Page size | Retrieved items |
 * |-----------|------|-----------|-----------------|
 * | 1         | 1    | 10        | 10              |
 * | 2         | 2    | 10        | 20              |
 * | 3         | 2    | 20        | 40              |
 * | 4         | 2    | 40        | 80              |
 * | 5         | 2    | 80        | 160             |
 * | 6         | 3    | 80        | 240             |
 * | 7         | 4    | 80        | 320             |
 * | 8         | 5    | 80        | 400             |
 * | 9         | 5    | 100       | 500             |
 * |-----------|------|-----------|-----------------|
 * ```
 * This algorithm means we can have the best of both worlds.
 * If there's a small number of changed pull requests since
 * our last update we'll do small requests that use minimal
 * bandwidth but if we encounter a repository where a lot
 * of PRs have changed since our last fetch (like a very
 * active repository or one we haven't fetched in a long time)
 * we'll spool up our page size in just a few requests and load
 * in bulk.
 *
 * As an example I used a very active internal repository and
 * asked for all PRs updated in the last 24 hours which was 320.
 * With the previous regime of fetching with a page size of 10
 * that obviously took 32 requests. With this new regime it
 * would take 7.
 */
export function getNextPagePathWithIncreasingPageSize(response: Response) {
  const nextPath = getNextPagePathFromLink(response)

  if (!nextPath) {
    return null
  }

  const { pathname, query } = URL.parse(nextPath, true)
  const { per_page, page } = query

  const pageSize = typeof per_page === 'string' ? parseInt(per_page, 10) : NaN
  const pageNumber = typeof page === 'string' ? parseInt(page, 10) : NaN

  if (!pageSize || !pageNumber) {
    return nextPath
  }

  // Confusing, but we're looking at the _next_ page path here
  // so the current is whatever came before it.
  const currentPage = pageNumber - 1

  // Number of received items thus far
  const received = currentPage * pageSize

  // Can't go above 100, that's the max the API will allow.
  const nextPageSize = Math.min(100, pageSize * 2)

  // Have we received exactly the amount of items
  // such that doubling the page size and loading the
  // second page would seamlessly fit? No sense going
  // above 100 since that's the max the API supports
  if (pageSize !== nextPageSize && received % nextPageSize === 0) {
    query.per_page = `${nextPageSize}`
    query.page = `${received / nextPageSize + 1}`
    return URL.format({ pathname, query })
  }

  return nextPath
}

/**
 * Returns an ISO 8601 time string with second resolution instead of
 * the standard javascript toISOString which returns millisecond
 * resolution. The GitHub API doesn't return dates with milliseconds
 * so we won't send any back either.
 */
function toGitHubIsoDateString(date: Date) {
  return date.toISOString().replace(/\.\d{3}Z$/, 'Z')
}

/**
 * An object for making authenticated requests to the GitHub API
 */
export class API {
  private static readonly TOKEN_INVALIDATED_EVENT = 'token-invalidated'

  private static readonly emitter = new Emitter()

  public static onTokenInvalidated(callback: (endpoint: string) => void) {
    API.emitter.on(API.TOKEN_INVALIDATED_EVENT, callback)
  }

  private static emitTokenInvalidated(endpoint: string) {
    API.emitter.emit(API.TOKEN_INVALIDATED_EVENT, endpoint)
  }

  /** Create a new API client from the given account. */
  public static fromAccount(account: Account): API {
    return new API(account.endpoint, account.token)
  }

  private endpoint: string
  private token: string

  /** Create a new API client for the endpoint, authenticated with the token. */
  public constructor(endpoint: string, token: string) {
    this.endpoint = endpoint
    this.token = token
  }

  /** Fetch a repo by its owner and name. */
  public async fetchRepository(
    owner: string,
    name: string
  ): Promise<IAPIFullRepository | null> {
    try {
      const response = await this.request('GET', `repos/${owner}/${name}`)
      if (response.status === HttpStatusCode.NotFound) {
        log.warn(`fetchRepository: '${owner}/${name}' returned a 404`)
        return null
      }
      return await parsedResponse<IAPIFullRepository>(response)
    } catch (e) {
      log.warn(`fetchRepository: an error occurred for '${owner}/${name}'`, e)
      return null
    }
  }

  /**
   * Fetch info needed to clone a repository. That includes:
   *  - The canonical clone URL for a repository, respecting the protocol
   *    preference if provided.
   *  - The default branch of the repository, in case the repository is empty.
   *    Only available for GitHub repositories.
   *
   * Returns null if the request returned a 404 (NotFound). NotFound doesn't
   * necessarily mean that the repository doesn't exist, it could exist and
   * the current user just doesn't have the permissions to see it. GitHub.com
   * doesn't differentiate between not found and permission denied for private
   * repositories as that would leak the existence of a private repository.
   *
   * Note that unlike `fetchRepository` this method will throw for all errors
   * except 404 NotFound responses.
   *
   * @param owner    The repository owner (nodejs in https://github.com/nodejs/node)
   * @param name     The repository name (node in https://github.com/nodejs/node)
   * @param protocol The preferred Git protocol (https or ssh)
   */
  public async fetchRepositoryCloneInfo(
    owner: string,
    name: string,
    protocol: GitProtocol | undefined
  ): Promise<IAPIRepositoryCloneInfo | null> {
    const response = await this.request('GET', `repos/${owner}/${name}`, {
      // Make sure we don't run into cache issues when fetching the repositories,
      // specially after repositories have been renamed.
      reloadCache: true,
    })

    if (response.status === HttpStatusCode.NotFound) {
      return null
    }

    const repo = await parsedResponse<IAPIRepository>(response)
    return {
      url: protocol === 'ssh' ? repo.ssh_url : repo.clone_url,
      defaultBranch: repo.default_branch,
    }
  }

  /** Fetch all repos a user has access to. */
  public async fetchRepositories(): Promise<ReadonlyArray<
    IAPIRepository
  > | null> {
    try {
      const repositories = await this.fetchAll<IAPIRepository>('user/repos')
      // "But wait, repositories can't have a null owner" you say.
      // Ordinarily you'd be correct but turns out there's super
      // rare circumstances where a user has been deleted but the
      // repository hasn't. Such cases are usually addressed swiftly
      // but in some cases like GitHub Enterprise instances
      // they can linger for longer than we'd like so we'll make
      // sure to exclude any such dangling repository, chances are
      // they won't be cloneable anyway.
      return repositories.filter(x => x.owner !== null)
    } catch (error) {
      log.warn(`fetchRepositories: ${error}`)
      return null
    }
  }

  /** Fetch the logged in account. */
  public async fetchAccount(): Promise<IAPIFullIdentity> {
    try {
      const response = await this.request('GET', 'user')
      const result = await parsedResponse<IAPIFullIdentity>(response)
      return result
    } catch (e) {
      log.warn(`fetchAccount: failed with endpoint ${this.endpoint}`, e)
      throw e
    }
  }

  /** Fetch the current user's emails. */
  public async fetchEmails(): Promise<ReadonlyArray<IAPIEmail>> {
    try {
      const response = await this.request('GET', 'user/emails')
      const result = await parsedResponse<ReadonlyArray<IAPIEmail>>(response)

      return Array.isArray(result) ? result : []
    } catch (e) {
      log.warn(`fetchEmails: failed with endpoint ${this.endpoint}`, e)
      return []
    }
  }

  /** Fetch all the orgs to which the user belongs. */
  public async fetchOrgs(): Promise<ReadonlyArray<IAPIOrganization>> {
    try {
      return await this.fetchAll<IAPIOrganization>('user/orgs')
    } catch (e) {
      log.warn(`fetchOrgs: failed with endpoint ${this.endpoint}`, e)
      return []
    }
  }

  /** Create a new GitHub repository with the given properties. */
  public async createRepository(
    org: IAPIOrganization | null,
    name: string,
    description: string,
    private_: boolean
  ): Promise<IAPIFullRepository> {
    try {
      const apiPath = org ? `orgs/${org.login}/repos` : 'user/repos'
      const response = await this.request('POST', apiPath, {
        body: {
          name,
          description,
          private: private_,
        },
      })

      return await parsedResponse<IAPIFullRepository>(response)
    } catch (e) {
      if (e instanceof APIError) {
        if (org !== null) {
          throw new Error(
            `Unable to create repository for organization '${org.login}'. Verify that the repository does not already exist and that you have permission to create a repository there.`
          )
        }
        throw e
      }

      log.error(`createRepository: failed with endpoint ${this.endpoint}`, e)
      throw new Error(
        `Unable to publish repository. Please check if you have an internet connection and try again.`
      )
    }
  }

  /** Create a new GitHub fork of this repository (owner and name) */
  public async forkRepository(
    owner: string,
    name: string
  ): Promise<IAPIFullRepository> {
    try {
      const apiPath = `/repos/${owner}/${name}/forks`
      const response = await this.request('POST', apiPath)
      return await parsedResponse<IAPIFullRepository>(response)
    } catch (e) {
      log.error(
        `forkRepository: failed to fork ${owner}/${name} at endpoint: ${this.endpoint}`,
        e
      )
      throw e
    }
  }

  /**
   * Fetch the issues with the given state that have been created or updated
   * since the given date.
   */
  public async fetchIssues(
    owner: string,
    name: string,
    state: 'open' | 'closed' | 'all',
    since: Date | null
  ): Promise<ReadonlyArray<IAPIIssue>> {
    const params: { [key: string]: string } = {
      state,
    }
    if (since && !isNaN(since.getTime())) {
      params.since = toGitHubIsoDateString(since)
    }

    const url = urlWithQueryString(`repos/${owner}/${name}/issues`, params)
    try {
      const issues = await this.fetchAll<IAPIIssue>(url)

      // PRs are issues! But we only want Really Seriously Issues.
      return issues.filter((i: any) => !i.pullRequest)
    } catch (e) {
      log.warn(`fetchIssues: failed for repository ${owner}/${name}`, e)
      throw e
    }
  }

  /** Fetch all open pull requests in the given repository. */
  public async fetchAllOpenPullRequests(owner: string, name: string) {
    const url = urlWithQueryString(`repos/${owner}/${name}/pulls`, {
      state: 'open',
    })
    try {
      return await this.fetchAll<IAPIPullRequest>(url)
    } catch (e) {
      log.warn(`failed fetching open PRs for repository ${owner}/${name}`, e)
      throw e
    }
  }

  /**
   * Fetch all pull requests in the given repository that have been
   * updated on or after the provided date.
   *
   * Note: The GitHub API doesn't support providing a last-updated
   * limitation for PRs like it does for issues so we're emulating
   * the issues API by sorting PRs descending by last updated and
   * only grab as many pages as we need to until we no longer receive
   * PRs that have been update more recently than the `since`
   * parameter.
   *
   * If there's more than `maxResults` updated PRs since the last time
   * we fetched this method will throw an error such that we can abort
   * this strategy and commence loading all open PRs instead.
   */
  public async fetchUpdatedPullRequests(
    owner: string,
    name: string,
    since: Date,
    // 320 is chosen because with a ramp-up page size starting with
    // a page size of 10 we'll reach 320 in exactly 7 pages. See
    // getNextPagePathWithIncreasingPageSize
    maxResults = 320
  ) {
    const sinceTime = since.getTime()
    const url = urlWithQueryString(`repos/${owner}/${name}/pulls`, {
      state: 'all',
      sort: 'updated',
      direction: 'desc',
    })

    try {
      const prs = await this.fetchAll<IAPIPullRequest>(url, {
        // We use a page size smaller than our default 100 here because we
        // expect that the majority use case will return much less than
        // 100 results. Given that as long as _any_ PR has changed we'll
        // get the full list back (PRs doesn't support ?since=) we want
        // to keep this number fairly conservative in order to not use
        // up bandwidth needlessly while balancing it such that we don't
        // have to use a lot of requests to update our database. We then
        // ramp up the page size (see getNextPagePathWithIncreasingPageSize)
        // if it turns out there's a lot of updated PRs.
        perPage: 10,
        getNextPagePath: getNextPagePathWithIncreasingPageSize,
        continue(results) {
          if (results.length >= maxResults) {
            throw new MaxResultsError('got max pull requests, aborting')
          }

          // Given that we sort the results in descending order by their
          // updated_at field we can safely say that if the last item
          // is modified after our sinceTime then haven't reached the
          // end of updated PRs.
          const last = results[results.length - 1]
          return last !== undefined && Date.parse(last.updated_at) > sinceTime
        },
        // We can't ignore errors here as that might mean that we haven't
        // retrieved enough pages to fully capture the changes since the
        // last time we updated. Ignoring errors here would mean that we'd
        // store an incorrect lastUpdated field in the database.
        suppressErrors: false,
      })
      return prs.filter(pr => Date.parse(pr.updated_at) >= sinceTime)
    } catch (e) {
      log.warn(`failed fetching updated PRs for repository ${owner}/${name}`, e)
      throw e
    }
  }

  /**
   * Fetch a single pull request in the given repository
   */
  public async fetchPullRequest(owner: string, name: string, prNumber: string) {
    try {
      const path = `/repos/${owner}/${name}/pulls/${prNumber}`
      const response = await this.request('GET', path)
      return await parsedResponse<IAPIPullRequest>(response)
    } catch (e) {
      log.warn(`failed fetching PR for ${owner}/${name}/pulls/${prNumber}`, e)
      throw e
    }
  }

  /**
   * Get the combined status for the given ref.
   */
  public async fetchCombinedRefStatus(
    owner: string,
    name: string,
    ref: string
  ): Promise<IAPIRefStatus | null> {
    const safeRef = encodeURIComponent(ref)
    const path = `repos/${owner}/${name}/commits/${safeRef}/status?per_page=100`
    const response = await this.request('GET', path)

    try {
      return await parsedResponse<IAPIRefStatus>(response)
    } catch (err) {
      log.debug(
        `Failed fetching check runs for ref ${ref} (${owner}/${name})`,
        err
      )
      return null
    }
  }

  /**
   * Get any check run results for the given ref.
   */
  public async fetchRefCheckRuns(
    owner: string,
    name: string,
    ref: string
  ): Promise<IAPIRefCheckRuns | null> {
    const safeRef = encodeURIComponent(ref)
    const path = `repos/${owner}/${name}/commits/${safeRef}/check-runs?per_page=100`
    const headers = {
      Accept: 'application/vnd.github.antiope-preview+json',
    }

    const response = await this.request('GET', path, { customHeaders: headers })

    try {
      return await parsedResponse<IAPIRefCheckRuns>(response)
    } catch (err) {
      log.debug(
        `Failed fetching check runs for ref ${ref} (${owner}/${name})`,
        err
      )
      return null
    }
  }

  /**
   * List workflow runs for a repository filtered by branch and event type of
   * pull_request
   */
  public async fetchPRWorkflowRuns(
    owner: string,
    name: string,
    branchName: string
  ): Promise<IAPIWorkflowRuns | null> {
    const path = `repos/${owner}/${name}/actions/runs?event=pull_request&branch=${encodeURIComponent(
      branchName
    )}`
    const customHeaders = {
      Accept: 'application/vnd.github.antiope-preview+json',
    }
    const response = await this.request('GET', path, { customHeaders })
    try {
      return await parsedResponse<IAPIWorkflowRuns>(response)
    } catch (err) {
      log.debug(
        `Failed fetching workflow runs for ${branchName} (${owner}/${name})`
      )
    }
    return null
  }

  /**
   * List workflow run jobs for a given workflow run
   */
  public async fetchWorkflowRunJobs(
    owner: string,
    name: string,
    workflowRunId: number
  ): Promise<IAPIWorkflowJobs | null> {
    const path = `repos/${owner}/${name}/actions/runs/${workflowRunId}/jobs`
    const customHeaders = {
      Accept: 'application/vnd.github.antiope-preview+json',
    }
    const response = await this.request('GET', path, {
      customHeaders,
    })
    try {
      return await parsedResponse<IAPIWorkflowJobs>(response)
    } catch (err) {
      log.debug(
        `Failed fetching workflow jobs (${owner}/${name}) workflow run: ${workflowRunId}`
      )
    }
    return null
  }

  /**
   * Get JSZip for a workflow run log archive.
   *
   * If it fails to retrieve or parse the zip file, it will return null.
   */
  public async fetchWorkflowRunJobLogs(logsUrl: string): Promise<JSZip | null> {
    const customHeaders = {
      Accept: 'application/vnd.github.antiope-preview+json',
    }
    const response = await this.request('GET', logsUrl, {
      customHeaders,
    })

    try {
      const zipBlob = await response.blob()
      return new JSZip().loadAsync(zipBlob)
    } catch (e) {
      // Sometimes a workflow provides a log url, but still returns a 404
      // because a log file doesn't make sense for the workflow. Thus, we just
      // want to fail without raising an error.
    }

    return null
  }

  /**
   * Triggers GitHub to rerequest an existing check suite, without pushing new
   * code to a repository.
   */
  public async rerequestCheckSuite(
    owner: string,
    name: string,
    checkSuiteId: number
  ): Promise<boolean> {
    const path = `/repos/${owner}/${name}/check-suites/${checkSuiteId}/rerequest`
    const response = await this.request('POST', path)

    try {
      return response.ok
    } catch (_) {
      log.debug(
        `Failed retry check suite id ${checkSuiteId} (${owner}/${name})`
      )
    }

    return false
  }

  /**
   * Gets a single check suite using its id
   */
  public async fetchCheckSuite(
    owner: string,
    name: string,
    checkSuiteId: number
  ): Promise<IAPICheckSuite | null> {
    const path = `/repos/${owner}/${name}/check-suites/${checkSuiteId}`
    const response = await this.request('GET', path)

    try {
      return await parsedResponse<IAPICheckSuite>(response)
    } catch (_) {
      log.debug(
        `[fetchCheckSuite] Failed fetch check suite id ${checkSuiteId} (${owner}/${name})`
      )
    }

    return null
  }

  /**
   * Get branch protection info to determine if a user can push to a given branch.
   *
   * Note: if request fails, the default returned value assumes full access for the user
   */
  public async fetchPushControl(
    owner: string,
    name: string,
    branch: string
  ): Promise<IAPIPushControl> {
    const path = `repos/${owner}/${name}/branches/${encodeURIComponent(
      branch
    )}/push_control`

    const headers: any = {
      Accept: 'application/vnd.github.phandalin-preview',
    }

    try {
      const response = await this.request('GET', path, {
        customHeaders: headers,
      })
      return await parsedResponse<IAPIPushControl>(response)
    } catch (err) {
      log.info(
        `[fetchPushControl] unable to check if branch is potentially pushable`,
        err
      )
      return {
        pattern: null,
        required_signatures: false,
        required_status_checks: [],
        required_approving_review_count: 0,
        required_linear_history: false,
        allow_actor: true,
        allow_deletions: true,
        allow_force_pushes: true,
      }
    }
  }

  public async fetchProtectedBranches(
    owner: string,
    name: string
  ): Promise<ReadonlyArray<IAPIBranch>> {
    const path = `repos/${owner}/${name}/branches?protected=true`
    try {
      const response = await this.request('GET', path)
      return await parsedResponse<IAPIBranch[]>(response)
    } catch (err) {
      log.info(
        `[fetchProtectedBranches] unable to list protected branches`,
        err
      )
      return new Array<IAPIBranch>()
    }
  }

  /**
   * Authenticated requests to a paginating resource such as issues.
   *
   * Follows the GitHub API hypermedia links to get the subsequent
   * pages when available, buffers all items and returns them in
   * one array when done.
   */
  private async fetchAll<T>(path: string, options?: IFetchAllOptions<T>) {
    const buf = new Array<T>()
    const opts: IFetchAllOptions<T> = { perPage: 100, ...options }
    const params = { per_page: `${opts.perPage}` }

    let nextPath: string | null = urlWithQueryString(path, params)
    do {
      const response: Response = await this.request('GET', nextPath)
      if (opts.suppressErrors !== false && !response.ok) {
        log.warn(`fetchAll: '${path}' returned a ${response.status}`)
        return buf
      }

      const items = await parsedResponse<ReadonlyArray<T>>(response)
      if (items) {
        buf.push(...items)
      }

      nextPath = opts.getNextPagePath
        ? opts.getNextPagePath(response)
        : getNextPagePathFromLink(response)
    } while (nextPath && (!opts.continue || opts.continue(buf)))

    return buf
  }

  /** Make an authenticated request to the client's endpoint with its token. */
  private async request(
    method: HTTPMethod,
    path: string,
    options: {
      body?: Object
      customHeaders?: Object
      reloadCache?: boolean
    } = {}
  ): Promise<Response> {
    const response = await request(
      this.endpoint,
      this.token,
      method,
      path,
      options.body,
      options.customHeaders,
      options.reloadCache
    )

    // Only consider invalid token when the status is 401 and the response has
    // the X-GitHub-Request-Id header, meaning it comes from GH(E) and not from
    // any kind of proxy/gateway. For more info see #12943
    // We're also not considering a token has been invalidated when the reason
    // behind a 401 is the fact that any kind of 2 factor auth is required.
    if (
      response.status === 401 &&
      response.headers.has('X-GitHub-Request-Id') &&
      !response.headers.has('X-GitHub-OTP')
    ) {
      API.emitTokenInvalidated(this.endpoint)
    }

    return response
  }

  /**
   * Get the allowed poll interval for fetching. If an error occurs it will
   * return null.
   */
  public async getFetchPollInterval(
    owner: string,
    name: string
  ): Promise<number | null> {
    const path = `repos/${owner}/${name}/git`
    try {
      const response = await this.request('HEAD', path)
      const interval = response.headers.get('x-poll-interval')
      if (interval) {
        const parsed = parseInt(interval, 10)
        return isNaN(parsed) ? null : parsed
      }
      return null
    } catch (e) {
      log.warn(`getFetchPollInterval: failed for ${owner}/${name}`, e)
      return null
    }
  }

  /** Fetch the mentionable users for the repository. */
  public async fetchMentionables(
    owner: string,
    name: string,
    etag: string | undefined
  ): Promise<IAPIMentionablesResponse | null> {
    // NB: this custom `Accept` is required for the `mentionables` endpoint.
    const headers: any = {
      Accept: 'application/vnd.github.jerry-maguire-preview',
    }

    if (etag !== undefined) {
      headers['If-None-Match'] = etag
    }

    try {
      const path = `repos/${owner}/${name}/mentionables/users`
      const response = await this.request('GET', path, {
        customHeaders: headers,
      })

      if (response.status === HttpStatusCode.NotFound) {
        log.warn(`fetchMentionables: '${path}' returned a 404`)
        return null
      }

      if (response.status === HttpStatusCode.NotModified) {
        return null
      }
      const users = await parsedResponse<ReadonlyArray<IAPIMentionableUser>>(
        response
      )
      const etag = response.headers.get('etag') || undefined
      return { users, etag }
    } catch (e) {
      log.warn(`fetchMentionables: failed for ${owner}/${name}`, e)
      return null
    }
  }

  /**
   * Retrieve the public profile information of a user with
   * a given username.
   */
  public async fetchUser(login: string): Promise<IAPIFullIdentity | null> {
    try {
      const response = await this.request(
        'GET',
        `users/${encodeURIComponent(login)}`
      )

      if (response.status === 404) {
        return null
      }

      return await parsedResponse<IAPIFullIdentity>(response)
    } catch (e) {
      log.warn(`fetchUser: failed with endpoint ${this.endpoint}`, e)
      throw e
    }
  }
}

export enum AuthorizationResponseKind {
  Authorized,
  Failed,
  TwoFactorAuthenticationRequired,
  UserRequiresVerification,
  PersonalAccessTokenBlocked,
  Error,
  EnterpriseTooOld,
  /**
   * The API has indicated that the user is required to go through
   * the web authentication flow.
   */
  WebFlowRequired,
}

export type AuthorizationResponse =
  | { kind: AuthorizationResponseKind.Authorized; token: string }
  | { kind: AuthorizationResponseKind.Failed; response: Response }
  | {
      kind: AuthorizationResponseKind.TwoFactorAuthenticationRequired
      type: AuthenticationMode
    }
  | { kind: AuthorizationResponseKind.Error; response: Response }
  | { kind: AuthorizationResponseKind.UserRequiresVerification }
  | { kind: AuthorizationResponseKind.PersonalAccessTokenBlocked }
  | { kind: AuthorizationResponseKind.EnterpriseTooOld }
  | { kind: AuthorizationResponseKind.WebFlowRequired }

/**
 * Create an authorization with the given login, password, and one-time
 * password.
 */
export async function createAuthorization(
  endpoint: string,
  login: string,
  password: string,
  oneTimePassword: string | null
): Promise<AuthorizationResponse> {
  const creds = Buffer.from(`${login}:${password}`, 'utf8').toString('base64')
  const authorization = `Basic ${creds}`
  const optHeader = oneTimePassword ? { 'X-GitHub-OTP': oneTimePassword } : {}

  const note = await getNote()

  const response = await request(
    endpoint,
    null,
    'POST',
    'authorizations',
    {
      scopes: getOAuthScopesForEndpoint(endpoint),
      client_id: ClientID,
      client_secret: ClientSecret,
      note: note,
      note_url: NoteURL,
      fingerprint: uuid(),
    },
    {
      Authorization: authorization,
      ...optHeader,
    }
  )

  try {
    const result = await parsedResponse<IAPIAuthorization>(response)
    if (result) {
      const token = result.token
      if (token && typeof token === 'string' && token.length) {
        return { kind: AuthorizationResponseKind.Authorized, token }
      }
    }
  } catch (e) {
    if (response.status === 401) {
      const otpResponse = response.headers.get('x-github-otp')
      if (otpResponse) {
        const pieces = otpResponse.split(';')
        if (pieces.length === 2) {
          const type = pieces[1].trim()
          switch (type) {
            case 'app':
              return {
                kind: AuthorizationResponseKind.TwoFactorAuthenticationRequired,
                type: AuthenticationMode.App,
              }
            case 'sms':
              return {
                kind: AuthorizationResponseKind.TwoFactorAuthenticationRequired,
                type: AuthenticationMode.Sms,
              }
            default:
              return { kind: AuthorizationResponseKind.Failed, response }
          }
        }
      }

      return { kind: AuthorizationResponseKind.Failed, response }
    }

    const apiError = e instanceof APIError && e.apiError
    if (apiError) {
      if (
        response.status === 403 &&
        apiError.message ===
          'This API can only be accessed with username and password Basic Auth'
      ) {
        // Authorization API does not support providing personal access tokens
        return { kind: AuthorizationResponseKind.PersonalAccessTokenBlocked }
      } else if (response.status === 410) {
        return { kind: AuthorizationResponseKind.WebFlowRequired }
      } else if (response.status === 422) {
        if (apiError.errors) {
          for (const error of apiError.errors) {
            const isExpectedResource =
              error.resource.toLowerCase() === 'oauthaccess'
            const isExpectedField = error.field.toLowerCase() === 'user'
            if (isExpectedField && isExpectedResource) {
              return {
                kind: AuthorizationResponseKind.UserRequiresVerification,
              }
            }
          }
        } else if (
          apiError.message === 'Invalid OAuth application client_id or secret.'
        ) {
          return { kind: AuthorizationResponseKind.EnterpriseTooOld }
        }
      }
    }
  }

  return { kind: AuthorizationResponseKind.Error, response }
}

/** Fetch the user authenticated by the token. */
export async function fetchUser(
  endpoint: string,
  token: string
): Promise<Account> {
  const api = new API(endpoint, token)
  try {
    const user = await api.fetchAccount()
    const emails = await api.fetchEmails()

    return new Account(
      user.login,
      endpoint,
      token,
      emails,
      user.avatar_url,
      user.id,
      user.name || user.login
    )
  } catch (e) {
    log.warn(`fetchUser: failed with endpoint ${endpoint}`, e)
    throw e
  }
}

/** Get metadata from the server. */
export async function fetchMetadata(
  endpoint: string
): Promise<IServerMetadata | null> {
  const url = `${endpoint}/meta`

  try {
    const response = await request(endpoint, null, 'GET', 'meta', undefined, {
      'Content-Type': 'application/json',
    })

    const result = await parsedResponse<IServerMetadata>(response)
    if (!result || result.verifiable_password_authentication === undefined) {
      return null
    }

    return result
  } catch (e) {
    log.error(
      `fetchMetadata: unable to load metadata from '${url}' as a fallback`,
      e
    )
    return null
  }
}

/** The note used for created authorizations. */
async function getNote(): Promise<string> {
  let localUsername = 'unknown'
  try {
    localUsername = await username()
  } catch (e) {
    log.error(
      `getNote: unable to resolve machine username, using '${localUsername}' as a fallback`,
      e
    )
  }

  return `GitHub Desktop on ${localUsername}@${OS.hostname()}`
}

/**
 * Map a repository's URL to the endpoint associated with it. For example:
 *
 * https://github.com/desktop/desktop -> https://api.github.com
 * http://github.mycompany.com/my-team/my-project -> http://github.mycompany.com/api
 */
export function getEndpointForRepository(url: string): string {
  const parsed = URL.parse(url)
  if (parsed.hostname === 'github.com') {
    return getDotComAPIEndpoint()
  } else {
    return `${parsed.protocol}//${parsed.hostname}/api`
  }
}

/**
 * Get the URL for the HTML site. For example:
 *
 * https://api.github.com -> https://github.com
 * http://github.mycompany.com/api -> http://github.mycompany.com/
 */
export function getHTMLURL(endpoint: string): string {
  if (envHTMLURL !== undefined) {
    return envHTMLURL
  }

  // In the case of GitHub.com, the HTML site lives on the parent domain.
  //  E.g., https://api.github.com -> https://github.com
  //
  // Whereas with Enterprise, it lives on the same domain but without the
  // API path:
  //  E.g., https://github.mycompany.com/api/v3 -> https://github.mycompany.com
  //
  // We need to normalize them.
  if (endpoint === getDotComAPIEndpoint() && !envEndpoint) {
    return 'https://github.com'
  } else {
    const parsed = URL.parse(endpoint)
    return `${parsed.protocol}//${parsed.hostname}`
  }
}

/**
 * Get the API URL for an HTML URL. For example:
 *
 * http://github.mycompany.com -> http://github.mycompany.com/api/v3
 */
export function getEnterpriseAPIURL(endpoint: string): string {
  const parsed = URL.parse(endpoint)
  return `${parsed.protocol}//${parsed.hostname}/api/v3`
}

/** Get github.com's API endpoint. */
export function getDotComAPIEndpoint(): string {
  // NOTE:
  // `DESKTOP_GITHUB_DOTCOM_API_ENDPOINT` only needs to be set if you are
  // developing against a local version of GitHub the Website, and need to debug
  // the server-side interaction. For all other cases you should leave this
  // unset.
  if (envEndpoint && envEndpoint.length > 0) {
    return envEndpoint
  }

  return 'https://api.github.com'
}

/** Get the account for the endpoint. */
export function getAccountForEndpoint(
  accounts: ReadonlyArray<Account>,
  endpoint: string
): Account | null {
  return accounts.find(a => a.endpoint === endpoint) || null
}

export function getOAuthAuthorizationURL(
  endpoint: string,
  state: string
): string {
  const urlBase = getHTMLURL(endpoint)
  const scopes = getOAuthScopesForEndpoint(endpoint)
  const scope = encodeURIComponent(scopes.join(' '))
  return `${urlBase}/login/oauth/authorize?client_id=${ClientID}&scope=${scope}&state=${state}`
}

export async function requestOAuthToken(
  endpoint: string,
  code: string
): Promise<string | null> {
  try {
    const urlBase = getHTMLURL(endpoint)
    const response = await request(
      urlBase,
      null,
      'POST',
      'login/oauth/access_token',
      {
        client_id: ClientID,
        client_secret: ClientSecret,
        code: code,
      }
    )
    const result = await parsedResponse<IAPIAccessToken>(response)
    return result.access_token
  } catch (e) {
    log.warn(`requestOAuthToken: failed with endpoint ${endpoint}`, e)
    return null
  }
}

function getOAuthScopesForEndpoint(endpoint: string) {
  return endpoint === getDotComAPIEndpoint()
    ? DotComOAuthScopes
    : EnterpriseOAuthScopes
}


text:
package org.apache.shindig.gadgets.servlet;

import org.apache.shindig.common.EasyMockTestCase;
import org.apache.shindig.common.servlet.HttpServletResponseRecorder;
import org.apache.shindig.gadgets.LockedDomainService;
import org.apache.shindig.gadgets.http.RequestPipeline;
import org.apache.shindig.gadgets.rewrite.CaptureRewriter;
import org.apache.shindig.gadgets.rewrite.DefaultRequestRewriterRegistry;
import org.apache.shindig.gadgets.rewrite.RequestRewriter;
import org.apache.shindig.gadgets.rewrite.RequestRewriterRegistry;

import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Contains everything needed for making servlet requests, plus a bunch of stuff that shouldn't be
 * here.
 *
 * TODO: Get rid of 'stuff that shouldn't be here'.
 */
public abstract class ServletTestFixture extends EasyMockTestCase {
  public final RequestPipeline pipeline = mock(RequestPipeline.class);
  public final CaptureRewriter rewriter = new CaptureRewriter();
  public final RequestRewriterRegistry rewriterRegistry
      = new DefaultRequestRewriterRegistry(Arrays.<RequestRewriter>asList(rewriter), null);
  public final HttpServletRequest request = mock(HttpServletRequest.class);
  public final HttpServletResponse response = mock(HttpServletResponse.class);
  public final HttpServletResponseRecorder recorder = new HttpServletResponseRecorder(response);
  public final LockedDomainService lockedDomainService = mock(LockedDomainService.class);
}

redpajama_set_name: RedPajamaGithub


text:
require 'digest/sha1'

#Processes a call to render() in a controller or template
module Brakeman::RenderHelper

  #Process s(:render, TYPE, OPTIONS)
  def process_render exp
    process_default exp
    @rendered = true
    case exp[1]
    when :action
      process_action exp[2][1], exp[3]
    when :default
      process_template template_name, exp[3]
    when :partial
      process_partial exp[2], exp[3]
    when :nothing
    end
    exp
  end

  #Processes layout
  def process_layout name = nil
    if name.nil? and defined? layout_name
      name = layout_name
    end

    return unless name

    process_template name, nil
  end

  #Determines file name for partial and then processes it
  def process_partial name, args
    if name == "" or !(string? name or symbol? name)
      return
    end

    names = name[1].to_s.split("/")
    names[-1] = "_" + names[-1]
    process_template template_name(names.join("/")), args
  end

  #Processes a given action
  def process_action name, args
    process_template template_name(name), args
  end

  #Processes a template, adding any instance variables
  #to its environment.
  def process_template name, args, called_from = nil
    Brakeman.debug "Rendering #{name} (#{called_from})"
    #Get scanned source for this template
    name = name.to_s.gsub(/^\//, "")
    template = @tracker.templates[name.to_sym]
    unless template
      Brakeman.debug "[Notice] No such template: #{name}"
      return
    end

    template_env = only_ivars

    #Hash the environment and the source of the template to avoid
    #pointlessly processing templates, which can become prohibitively
    #expensive in terms of time and memory.
    digest = Digest::SHA1.new.update(template_env.instance_variable_get(:@env).to_a.sort.to_s << name).to_s.to_sym

    if @tracker.template_cache.include? digest
      #Already processed this template with identical environment
      return
    else
      @tracker.template_cache << digest

      options = get_options args

      #Process layout
      if string? options[:layout]
        process_template "layouts/#{options[:layout][1]}", nil
      elsif node_type? options[:layout], :false
        #nothing
      elsif not template[:name].to_s.match(/[^\/_][^\/]+$/)
        #Don't do this for partials

        process_layout
      end

      if hash? options[:locals]
        hash_iterate options[:locals] do |key, value|
          template_env[Sexp.new(:call, nil, key[1], Sexp.new(:arglist))] = value
        end
      end

      if options[:collection]

        #The collection name is the name of the partial without the leading
        #underscore.
        variable = template[:name].to_s.match(/[^\/_][^\/]+$/)[0].to_sym

        #Unless the :as => :variable_name option is used
        if options[:as]
          if string? options[:as] or symbol? options[:as]
            variable = options[:as][1].to_sym
          end
        end

        template_env[Sexp.new(:call, nil, variable, Sexp.new(:arglist))] = Sexp.new(:call, Sexp.new(:const, Brakeman::Tracker::UNKNOWN_MODEL), :new, Sexp.new(:arglist))
      end

      #Set original_line for values so it is clear
      #that values came from another file
      template_env.all.each do |var, value|
        unless value.original_line
          value.original_line = value.line
        end
      end

      #Run source through AliasProcessor with instance variables from the
      #current environment.
      #TODO: Add in :locals => { ... } to environment
      src = Brakeman::TemplateAliasProcessor.new(@tracker, template).process_safely(template[:src], template_env)

      #Run alias-processed src through the template processor to pull out
      #information and outputs.
      #This information will be stored in tracker.templates, but with a name
      #specifying this particular route. The original source should remain
      #pristine (so it can be processed within other environments).
      @tracker.processor.process_template name, src, template[:type], called_from
    end
  end

  #Override to process name, such as adding the controller name.
  def template_name name
    raise "RenderHelper#template_name should be overridden."
  end

  #Turn options Sexp into hash
  def get_options args
    options = {}
    return options unless hash? args

    hash_iterate args do |key, value|
      if symbol? key
        options[key[1]] = value
      end
    end

    options
  end
end


text:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace OpenClassic.Server.Util
{
    public class SingleObjectOrArrayJsonConverter<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(List<T>));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var results = new List<T>();

            var token = JToken.Load(reader);
            if (token.Type == JTokenType.Array)
            {
                var multipleObjects = token.ToObject<List<T>>();

                results.AddRange(multipleObjects);
            }
            else if (token.Type == JTokenType.Object)
            {
                var singleObject = token.ToObject<T>();

                results.Add(singleObject);
            }

            return results;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {

        }
    }
}


text:
var searchData=
[
  ['privatefuncptrholder',['privateFuncPtrHolder',['../structHackPrivate_1_1privateFuncPtrHolder.html',1,'HackPrivate']]]
];


text:
import { createSelector } from "reselect";
import { flat as selectDocs } from "./docs";
import { flat as selectPool } from "./pool";
import createRelationSelector from "./relation";

const selectVirtual = createSelector(
  selectDocs,
  docs => {
    return docs.filter(d => {
      const {manifest} = d;
      const {options = {}} = manifest;
      const {query = ""} = options;
      return Boolean(query);
    });
  }
);

const selectItem = createSelector(
  selectPool,
  state => state.id,
  (pool, id) => {
    const item = pool.find(item => id === `${item.contentType}/${item.id}`);

    if (item) {
      return item;
    }

    if (id === '/') {
      return pool[0];
    }
  }
);

export default selectItem;

const filter = hidden => {
  return hidden
    ? item => (item.manifest.options || {}).hidden !== true
    : i => i;
};

const selectFilter = createSelector(state => state.hide, hide => filter(hide));

const relation = key => createRelationSelector(key, selectItem, selectFilter);

export const selectDependencies = relation("dependencies");
export const selectDependents = relation("dependents");

export const selectManifest = createSelector(
  selectItem,
  item => (item ? JSON.stringify(item.manifest, null, "  ") : "")
);

export const selectContentType = createSelector(
  selectItem,
  item => (item ? item.contentType : "")
);

export const selectType = createSelector(
  selectItem,
  item => (item ? item.type : "")
);

export const selectActive = createSelector(
  selectItem,
  state => state.searchEnabled,
  (item, search) => !search && item !== null && typeof item !== "undefined"
);

export const selectIcon = createSelector(
  selectItem,
  item => (item ? item.manifest.icon || item.type : "")
);

export const selectName = createSelector(
  selectItem,
  item => (item ? item.manifest.displayName : "")
);

export const selectTags = createSelector(
  selectItem,
  item => (item ? item.manifest.tags || [] : [])
);

export const selectVersion = createSelector(
  selectItem,
  item => (item ? item.manifest.version : "")
);

export const selectFlag = createSelector(
  selectItem,
  item => (item ? item.manifest.flag : "")
);

export const selectId = createSelector(
  selectItem,
  item => (item ? item.id : "")
);

export const selectContents = createSelector(selectItem, item => {
  if (!item) {
    return null;
  }
  return typeof item.contents === "string" ? item.contents : null;
});

export const selectDescription = createSelector(
  selectItem,
  item => (item ? item.manifest.description : "")
);


text:
using content::DownloadManager;
using content::OpenURLParams;
using content::Referrer;
using content::UserMetricsAction;

namespace options2 {

AdvancedOptionsHandler::AdvancedOptionsHandler() {

#if(!defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN))
  // On Windows, we need the PDF plugin which is only guaranteed to exist on
  // Google Chrome builds. Use a command-line switch for Windows non-Google
  //  Chrome builds.
  cloud_print_connector_ui_enabled_ =
      CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kEnableCloudPrintProxy);
#elif(!defined(OS_CHROMEOS))
  // Always enabled for Mac, Linux and Google Chrome Windows builds.
  // Never enabled for Chrome OS, we don't even need to indicate it.
  cloud_print_connector_ui_enabled_ = true;
#endif
}

AdvancedOptionsHandler::~AdvancedOptionsHandler() {
  // There may be pending file dialogs, we need to tell them that we've gone
  // away so they don't try and call back to us.
  if (select_folder_dialog_.get())
    select_folder_dialog_->ListenerDestroyed();
}

void AdvancedOptionsHandler::GetLocalizedValues(
    DictionaryValue* localized_strings) {
  DCHECK(localized_strings);

  static OptionsStringResource resources[] = {
    { "downloadLocationGroupName",
      IDS_OPTIONS_DOWNLOADLOCATION_GROUP_NAME },
    { "downloadLocationChangeButton",
      IDS_OPTIONS_DOWNLOADLOCATION_CHANGE_BUTTON },
    { "downloadLocationBrowseTitle",
      IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_TITLE },
    { "downloadLocationBrowseWindowTitle",
      IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_WINDOW_TITLE },
    { "downloadLocationAskForSaveLocation",
      IDS_OPTIONS_DOWNLOADLOCATION_ASKFORSAVELOCATION },
    { "autoOpenFileTypesInfo",
      IDS_OPTIONS_OPEN_FILE_TYPES_AUTOMATICALLY },
    { "autoOpenFileTypesResetToDefault",
      IDS_OPTIONS_AUTOOPENFILETYPES_RESETTODEFAULT },
    { "translateEnableTranslate",
      IDS_OPTIONS_TRANSLATE_ENABLE_TRANSLATE },
    { "certificatesManageButton",
      IDS_OPTIONS_CERTIFICATES_MANAGE_BUTTON },
    { "proxiesLabel",
      IDS_OPTIONS_PROXIES_LABEL },
#if !defined(OS_CHROMEOS)
    { "proxiesConfigureButton",
      IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON },
#endif
    { "safeBrowsingEnableProtection",
      IDS_OPTIONS_SAFEBROWSING_ENABLEPROTECTION },
    { "sslGroupDescription",
      IDS_OPTIONS_SSL_GROUP_DESCRIPTION },
    { "sslCheckRevocation",
      IDS_OPTIONS_SSL_CHECKREVOCATION },
    { "networkPredictionEnabledDescription",
      IDS_NETWORK_PREDICTION_ENABLED_DESCRIPTION },
    { "privacyContentSettingsButton",
      IDS_OPTIONS_PRIVACY_CONTENT_SETTINGS_BUTTON },
    { "privacyClearDataButton",
      IDS_OPTIONS_PRIVACY_CLEAR_DATA_BUTTON },
    { "linkDoctorPref",
      IDS_OPTIONS_LINKDOCTOR_PREF },
    { "suggestPref",
      IDS_OPTIONS_SUGGEST_PREF },
    { "tabsToLinksPref",
      IDS_OPTIONS_TABS_TO_LINKS_PREF },
    { "fontSettingsInfo",
      IDS_OPTIONS_FONTSETTINGS_INFO },
    { "defaultZoomFactorLabel",
      IDS_OPTIONS_DEFAULT_ZOOM_LEVEL_LABEL },
    { "defaultFontSizeLabel",
      IDS_OPTIONS_DEFAULT_FONT_SIZE_LABEL },
    { "fontSizeLabelVerySmall",
      IDS_OPTIONS_FONT_SIZE_LABEL_VERY_SMALL },
    { "fontSizeLabelSmall",
      IDS_OPTIONS_FONT_SIZE_LABEL_SMALL },
    { "fontSizeLabelMedium",
      IDS_OPTIONS_FONT_SIZE_LABEL_MEDIUM },
    { "fontSizeLabelLarge",
      IDS_OPTIONS_FONT_SIZE_LABEL_LARGE },
    { "fontSizeLabelVeryLarge",
      IDS_OPTIONS_FONT_SIZE_LABEL_VERY_LARGE },
    { "fontSizeLabelCustom",
      IDS_OPTIONS_FONT_SIZE_LABEL_CUSTOM },
    { "fontSettingsCustomizeFontsButton",
      IDS_OPTIONS_FONTSETTINGS_CUSTOMIZE_FONTS_BUTTON },
    { "languageAndSpellCheckSettingsButton",
#if defined(OS_CHROMEOS)
      IDS_OPTIONS_SETTINGS_LANGUAGES_CUSTOMIZE },
#else
      IDS_OPTIONS_LANGUAGE_AND_SPELLCHECK_BUTTON },
#endif
    { "advancedSectionTitlePrivacy",
      IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY },
    { "advancedSectionTitleContent",
      IDS_OPTIONS_ADVANCED_SECTION_TITLE_CONTENT },
    { "advancedSectionTitleSecurity",
      IDS_OPTIONS_ADVANCED_SECTION_TITLE_SECURITY },
    { "advancedSectionTitleNetwork",
      IDS_OPTIONS_ADVANCED_SECTION_TITLE_NETWORK },
    { "advancedSectionTitleLanguages",
      IDS_OPTIONS_ADVANCED_SECTION_TITLE_LANGUAGES },
    { "translateEnableTranslate",
      IDS_OPTIONS_TRANSLATE_ENABLE_TRANSLATE },
    { "enableLogging",
      IDS_OPTIONS_ENABLE_LOGGING },
    { "improveBrowsingExperience",
      IDS_OPTIONS_IMPROVE_BROWSING_EXPERIENCE },
    { "disableWebServices",
      IDS_OPTIONS_DISABLE_WEB_SERVICES },
    { "advancedSectionTitleCloudPrint",
      IDS_GOOGLE_CLOUD_PRINT },
#if !defined(OS_CHROMEOS)
    { "cloudPrintConnectorEnabledManageButton",
      IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_MANAGE_BUTTON},
    { "cloudPrintConnectorEnablingButton",
      IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLING_BUTTON },
#endif
#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
    { "advancedSectionTitleBackground",
      IDS_OPTIONS_ADVANCED_SECTION_TITLE_BACKGROUND },
    { "backgroundModeCheckbox",
      IDS_OPTIONS_BACKGROUND_ENABLE_BACKGROUND_MODE },
#endif
    { "passwordsAndAutofillGroupName",
      IDS_OPTIONS_PASSWORDS_AND_FORMS_GROUP_NAME },
    { "passwordManagerEnabled",
      IDS_OPTIONS_PASSWORD_MANAGER_ENABLE },
    { "managePasswords",
      IDS_OPTIONS_PASSWORDS_MANAGE_PASSWORDS_LINK },
    { "autofillEnabled",
      IDS_OPTIONS_AUTOFILL_ENABLE },
    { "manageAutofillSettings",
      IDS_OPTIONS_MANAGE_AUTOFILL_SETTINGS_LINK },
#if defined(OS_CHROMEOS)
    { "datetimeTitle",
      IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME },
    { "timezone",
      IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION },
    { "use24HourClock",
      IDS_OPTIONS_SETTINGS_USE_24HOUR_CLOCK_DESCRIPTION },
    { "accessibilityTitle",
      IDS_OPTIONS_SETTINGS_SECTION_TITLE_ACCESSIBILITY },
    { "accessibilitySpokenFeedback",
      IDS_OPTIONS_SETTINGS_ACCESSIBILITY_DESCRIPTION },
    { "accessibilityHighContrast",
      IDS_OPTIONS_SETTINGS_ACCESSIBILITY_HIGH_CONTRAST_DESCRIPTION },
    { "accessibilityScreenMagnifier",
      IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_DESCRIPTION },
    { "accessibilityVirtualKeyboard",
      IDS_OPTIONS_SETTINGS_ACCESSIBILITY_VIRTUAL_KEYBOARD_DESCRIPTION },
#endif
  };

  RegisterStrings(localized_strings, resources, arraysize(resources));
  RegisterCloudPrintStrings(localized_strings);
  RegisterTitle(localized_strings, "advancedPage",
                IDS_OPTIONS2_ADVANCED_TAB_LABEL);

  localized_strings->SetString("privacyLearnMoreURL",
                               chrome::kPrivacyLearnMoreURL);

  localized_strings->SetString(
      "languageSectionLabel",
      l10n_util::GetStringFUTF16(
          IDS_OPTIONS_ADVANCED_LANGUAGE_LABEL,
          l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)));

#if defined(OS_CHROMEOS)
  localized_strings->SetString("cloudPrintLearnMoreURL",
                               chrome::kCloudPrintLearnMoreURL);

  // TODO(pastarmovj): replace this with a call to the CrosSettings list
  // handling functionality to come.
  localized_strings->Set("timezoneList",
      static_cast<chromeos::options2::SystemSettingsProvider*>(
          chromeos::CrosSettings::Get()->GetProvider(
              chromeos::kSystemTimezone))->GetTimezoneList());
#endif
#if defined(OS_MACOSX)
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  if (profile_manager->GetNumberOfProfiles() > 1) {
    localized_strings->SetString("macPasswordsWarning",
        l10n_util::GetStringUTF16(IDS_OPTIONS_PASSWORDS_MAC_WARNING));
  }
#endif
}

void AdvancedOptionsHandler::RegisterCloudPrintStrings(
    DictionaryValue* localized_strings) {
#if defined(OS_CHROMEOS)
  localized_strings->SetString("cloudPrintChromeosOptionLabel",
      l10n_util::GetStringFUTF16(
      IDS_CLOUD_PRINT_CHROMEOS_OPTION_LABEL,
      l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
  localized_strings->SetString("cloudPrintChromeosOptionButton",
      l10n_util::GetStringFUTF16(
      IDS_CLOUD_PRINT_CHROMEOS_OPTION_BUTTON,
      l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
#else
  localized_strings->SetString("cloudPrintConnectorDisabledLabel",
      l10n_util::GetStringFUTF16(
      IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
      l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
  localized_strings->SetString("cloudPrintConnectorDisabledButton",
      l10n_util::GetStringFUTF16(
      IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_DISABLED_BUTTON,
      l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
  localized_strings->SetString("cloudPrintConnectorEnabledButton",
      l10n_util::GetStringFUTF16(
      IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_BUTTON,
      l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
#endif
}

void AdvancedOptionsHandler::Initialize() {
  DCHECK(web_ui());
  SetupMetricsReportingCheckbox();
  SetupMetricsReportingSettingVisibility();
  SetupFontSizeSelector();
  SetupPageZoomSelector();
  SetupAutoOpenFileTypesDisabledAttribute();
  SetupProxySettingsSection();
  SetupSSLConfigSettings();
#if !defined(OS_CHROMEOS)
  if (cloud_print_connector_ui_enabled_) {
    SetupCloudPrintConnectorSection();
    RefreshCloudPrintStatusFromService();
  } else {
    RemoveCloudPrintConnectorSection();
  }
#endif
#if defined(OS_CHROMEOS)
  SetupAccessibilityFeatures();
#endif
#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
  SetupBackgroundModeSettings();
#endif

}

void AdvancedOptionsHandler::RegisterMessages() {
  // Register for preferences that we need to observe manually.  These have
  // special behaviors that aren't handled by the standard prefs UI.
  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
#if !defined(OS_CHROMEOS)
  enable_metrics_recording_.Init(prefs::kMetricsReportingEnabled,
                                 g_browser_process->local_state(), this);
  cloud_print_connector_email_.Init(prefs::kCloudPrintEmail, prefs, this);
  cloud_print_connector_enabled_.Init(prefs::kCloudPrintProxyEnabled,
                                      prefs,
                                      this);
#endif

  rev_checking_enabled_.Init(prefs::kCertRevocationCheckingEnabled,
                             g_browser_process->local_state(), this);

#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
  background_mode_enabled_.Init(prefs::kBackgroundModeEnabled,
                                g_browser_process->local_state(),
                                this);
#endif

  auto_open_files_.Init(prefs::kDownloadExtensionsToOpen, prefs, this);
  default_font_size_.Init(prefs::kWebKitGlobalDefaultFontSize, prefs, this);
  default_zoom_level_.Init(prefs::kDefaultZoomLevel, prefs, this);
#if !defined(OS_CHROMEOS)
  proxy_prefs_.reset(
      PrefSetObserver::CreateProxyPrefSetObserver(prefs, this));
#endif  // !defined(OS_CHROMEOS)

  // Setup handlers specific to this panel.
  web_ui()->RegisterMessageCallback(
      "selectDownloadLocation",
      base::Bind(&AdvancedOptionsHandler::HandleSelectDownloadLocation,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "autoOpenFileTypesAction",
      base::Bind(&AdvancedOptionsHandler::HandleAutoOpenButton,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "defaultFontSizeAction",
      base::Bind(&AdvancedOptionsHandler::HandleDefaultFontSize,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "defaultZoomFactorAction",
      base::Bind(&AdvancedOptionsHandler::HandleDefaultZoomFactor,
                 base::Unretained(this)));
#if !defined(OS_CHROMEOS)
  web_ui()->RegisterMessageCallback(
      "metricsReportingCheckboxAction",
      base::Bind(&AdvancedOptionsHandler::HandleMetricsReportingCheckbox,
                 base::Unretained(this)));
#endif
#if !defined(USE_NSS) && !defined(USE_OPENSSL)
  web_ui()->RegisterMessageCallback(
      "showManageSSLCertificates",
      base::Bind(&AdvancedOptionsHandler::ShowManageSSLCertificates,
                 base::Unretained(this)));
#endif
  web_ui()->RegisterMessageCallback(
      "showCloudPrintManagePage",
      base::Bind(&AdvancedOptionsHandler::ShowCloudPrintManagePage,
                 base::Unretained(this)));
#if !defined(OS_CHROMEOS)
  if (cloud_print_connector_ui_enabled_) {
    web_ui()->RegisterMessageCallback(
        "showCloudPrintSetupDialog",
        base::Bind(&AdvancedOptionsHandler::ShowCloudPrintSetupDialog,
                   base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        "disableCloudPrintConnector",
        base::Bind(&AdvancedOptionsHandler::HandleDisableCloudPrintConnector,
                   base::Unretained(this)));
  }
  web_ui()->RegisterMessageCallback(
      "showNetworkProxySettings",
      base::Bind(&AdvancedOptionsHandler::ShowNetworkProxySettings,
                 base::Unretained(this)));
#endif
  web_ui()->RegisterMessageCallback(
      "checkRevocationCheckboxAction",
      base::Bind(&AdvancedOptionsHandler::HandleCheckRevocationCheckbox,
                 base::Unretained(this)));
#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
  web_ui()->RegisterMessageCallback(
      "backgroundModeAction",
      base::Bind(&AdvancedOptionsHandler::HandleBackgroundModeCheckbox,
                 base::Unretained(this)));
#endif
#if defined(OS_CHROMEOS)
  web_ui()->RegisterMessageCallback(
      "spokenFeedbackChange",
      base::Bind(&AdvancedOptionsHandler::SpokenFeedbackChangeCallback,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "highContrastChange",
      base::Bind(&AdvancedOptionsHandler::HighContrastChangeCallback,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "screenMagnifierChange",
      base::Bind(&AdvancedOptionsHandler::ScreenMagnifierChangeCallback,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "virtualKeyboardChange",
      base::Bind(&AdvancedOptionsHandler::VirtualKeyboardChangeCallback,
                 base::Unretained(this)));
#endif
}

void AdvancedOptionsHandler::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  if (type == chrome::NOTIFICATION_PREF_CHANGED) {
    std::string* pref_name = content::Details<std::string>(details).ptr();
    if (*pref_name == prefs::kDownloadExtensionsToOpen) {
      SetupAutoOpenFileTypesDisabledAttribute();
#if !defined(OS_CHROMEOS)
    } else if (proxy_prefs_->IsObserved(*pref_name)) {
      SetupProxySettingsSection();
#endif  // !defined(OS_CHROMEOS)
    } else if ((*pref_name == prefs::kCloudPrintEmail) ||
               (*pref_name == prefs::kCloudPrintProxyEnabled)) {
#if !defined(OS_CHROMEOS)
      if (cloud_print_connector_ui_enabled_)
        SetupCloudPrintConnectorSection();
#endif
    } else if (*pref_name == prefs::kWebKitGlobalDefaultFontSize) {
      SetupFontSizeSelector();
    } else if (*pref_name == prefs::kDefaultZoomLevel) {
      SetupPageZoomSelector();
#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
    } else if (*pref_name == prefs::kBackgroundModeEnabled) {
      SetupBackgroundModeSettings();
#endif
    }
  }
}

void AdvancedOptionsHandler::HandleSelectDownloadLocation(
    const ListValue* args) {
  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
  select_folder_dialog_ = SelectFileDialog::Create(this);
  select_folder_dialog_->SelectFile(
      SelectFileDialog::SELECT_FOLDER,
      l10n_util::GetStringUTF16(IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_TITLE),
      pref_service->GetFilePath(prefs::kDownloadDefaultDirectory),
      NULL, 0, FILE_PATH_LITERAL(""), web_ui()->GetWebContents(),
      web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
}

void AdvancedOptionsHandler::FileSelected(const FilePath& path, int index,
                                          void* params) {
  content::RecordAction(UserMetricsAction("Options_SetDownloadDirectory"));
  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
  pref_service->SetFilePath(prefs::kDownloadDefaultDirectory, path);
}

void AdvancedOptionsHandler::OnCloudPrintSetupClosed() {
#if !defined(OS_CHROMEOS)
  if (cloud_print_connector_ui_enabled_)
    SetupCloudPrintConnectorSection();
#endif
}

void AdvancedOptionsHandler::HandleAutoOpenButton(const ListValue* args) {
  content::RecordAction(UserMetricsAction("Options_ResetAutoOpenFiles"));
  DownloadManager* manager =
      web_ui()->GetWebContents()->GetBrowserContext()->GetDownloadManager();
  if (manager)
    DownloadPrefs::FromDownloadManager(manager)->ResetAutoOpen();
}

void AdvancedOptionsHandler::HandleMetricsReportingCheckbox(
    const ListValue* args) {
#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
  std::string checked_str = UTF16ToUTF8(ExtractStringValue(args));
  bool enabled = checked_str == "true";
  content::RecordAction(
      enabled ?
          UserMetricsAction("Options_MetricsReportingCheckbox_Enable") :
          UserMetricsAction("Options_MetricsReportingCheckbox_Disable"));
  bool is_enabled = OptionsUtil::ResolveMetricsReportingEnabled(enabled);
  enable_metrics_recording_.SetValue(is_enabled);
  SetupMetricsReportingCheckbox();
#endif
}

void AdvancedOptionsHandler::HandleDefaultFontSize(const ListValue* args) {
  int font_size;
  if (ExtractIntegerValue(args, &font_size)) {
    if (font_size > 0) {
      default_font_size_.SetValue(font_size);
      SetupFontSizeSelector();
    }
  }
}

void AdvancedOptionsHandler::HandleDefaultZoomFactor(const ListValue* args) {
  double zoom_factor;
  if (ExtractDoubleValue(args, &zoom_factor)) {
    default_zoom_level_.SetValue(
        WebKit::WebView::zoomFactorToZoomLevel(zoom_factor));
  }
}

void AdvancedOptionsHandler::HandleCheckRevocationCheckbox(
    const ListValue* args) {
  std::string checked_str = UTF16ToUTF8(ExtractStringValue(args));
  bool enabled = checked_str == "true";
  content::RecordAction(
      enabled ?
          UserMetricsAction("Options_CheckCertRevocation_Enable") :
          UserMetricsAction("Options_CheckCertRevocation_Disable"));
  rev_checking_enabled_.SetValue(enabled);
}

#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
void AdvancedOptionsHandler::HandleBackgroundModeCheckbox(
    const ListValue* args) {
  std::string checked_str = UTF16ToUTF8(ExtractStringValue(args));
  bool enabled = checked_str == "true";
  content::RecordAction(enabled ?
      UserMetricsAction("Options_BackgroundMode_Enable") :
      UserMetricsAction("Options_BackgroundMode_Disable"));
  background_mode_enabled_.SetValue(enabled);
}

void AdvancedOptionsHandler::SetupBackgroundModeSettings() {
    base::FundamentalValue checked(background_mode_enabled_.GetValue());
    web_ui()->CallJavascriptFunction(
        "AdvancedOptions.SetBackgroundModeCheckboxState", checked);
}
#endif

#if !defined(OS_CHROMEOS)
void AdvancedOptionsHandler::ShowNetworkProxySettings(const ListValue* args) {
  content::RecordAction(UserMetricsAction("Options_ShowProxySettings"));
  AdvancedOptionsUtilities::ShowNetworkProxySettings(
      web_ui()->GetWebContents());
}
#endif

#if !defined(USE_NSS) && !defined(USE_OPENSSL)
void AdvancedOptionsHandler::ShowManageSSLCertificates(const ListValue* args) {
  content::RecordAction(UserMetricsAction("Options_ManageSSLCertificates"));
  AdvancedOptionsUtilities::ShowManageSSLCertificates(
      web_ui()->GetWebContents());
}
#endif

void AdvancedOptionsHandler::ShowCloudPrintManagePage(const ListValue* args) {
  content::RecordAction(UserMetricsAction("Options_ManageCloudPrinters"));
  // Open a new tab in the current window for the management page.
  Profile* profile = Profile::FromWebUI(web_ui());
  OpenURLParams params(
      CloudPrintURL(profile).GetCloudPrintServiceManageURL(), Referrer(),
      NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false);
  web_ui()->GetWebContents()->OpenURL(params);
}

#if !defined(OS_CHROMEOS)
void AdvancedOptionsHandler::ShowCloudPrintSetupDialog(const ListValue* args) {
  content::RecordAction(UserMetricsAction("Options_EnableCloudPrintProxy"));
  // Open the connector enable page in the current tab.
  Profile* profile = Profile::FromWebUI(web_ui());
  OpenURLParams params(
      CloudPrintURL(profile).GetCloudPrintServiceEnableURL(
          CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()),
      Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK, false);
  web_ui()->GetWebContents()->OpenURL(params);
}

void AdvancedOptionsHandler::HandleDisableCloudPrintConnector(
    const ListValue* args) {
  content::RecordAction(
      UserMetricsAction("Options_DisableCloudPrintProxy"));
  CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
      DisableForUser();
}

void AdvancedOptionsHandler::RefreshCloudPrintStatusFromService() {
  if (cloud_print_connector_ui_enabled_)
    CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
        RefreshStatusFromService();
}

void AdvancedOptionsHandler::SetupCloudPrintConnectorSection() {
  Profile* profile = Profile::FromWebUI(web_ui());
  if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) {
    cloud_print_connector_ui_enabled_ = false;
    RemoveCloudPrintConnectorSection();
    return;
  }

  bool cloud_print_connector_allowed =
      !cloud_print_connector_enabled_.IsManaged() ||
      cloud_print_connector_enabled_.GetValue();
  base::FundamentalValue allowed(cloud_print_connector_allowed);

  std::string email;
  if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
      cloud_print_connector_allowed) {
    email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
  }
  base::FundamentalValue disabled(email.empty());

  string16 label_str;
  if (email.empty()) {
    label_str = l10n_util::GetStringFUTF16(
        IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
        l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
  } else {
    label_str = l10n_util::GetStringFUTF16(
        IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
        l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
        UTF8ToUTF16(email));
  }
  StringValue label(label_str);

  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.SetupCloudPrintConnectorSection", disabled, label,
      allowed);
}

void AdvancedOptionsHandler::RemoveCloudPrintConnectorSection() {
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.RemoveCloudPrintConnectorSection");
}
#endif

#if defined(OS_CHROMEOS)
void AdvancedOptionsHandler::SpokenFeedbackChangeCallback(
    const ListValue* args) {
  bool enabled = false;
  args->GetBoolean(0, &enabled);

  chromeos::accessibility::EnableAccessibility(enabled, NULL);
}

void AdvancedOptionsHandler::HighContrastChangeCallback(const ListValue* args) {
  bool enabled = false;
  args->GetBoolean(0, &enabled);

  chromeos::accessibility::EnableHighContrast(enabled);
}

void AdvancedOptionsHandler::ScreenMagnifierChangeCallback(
    const ListValue* args) {
  bool enabled = false;
  args->GetBoolean(0, &enabled);

  chromeos::accessibility::EnableScreenMagnifier(enabled);
}

void AdvancedOptionsHandler::VirtualKeyboardChangeCallback(
    const ListValue* args) {
  bool enabled = false;
  args->GetBoolean(0, &enabled);

  chromeos::accessibility::EnableVirtualKeyboard(enabled);
}

void AdvancedOptionsHandler::SetupAccessibilityFeatures() {
  PrefService* pref_service = g_browser_process->local_state();
  base::FundamentalValue spoken_feedback_enabled(
      pref_service->GetBoolean(prefs::kSpokenFeedbackEnabled));
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.setSpokenFeedbackCheckboxState",
      spoken_feedback_enabled);
  base::FundamentalValue high_contrast_enabled(
      pref_service->GetBoolean(prefs::kHighContrastEnabled));
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.setHighContrastCheckboxState",
      high_contrast_enabled);
  base::FundamentalValue screen_magnifier_enabled(
      pref_service->GetBoolean(prefs::kScreenMagnifierEnabled));
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.setScreenMagnifierCheckboxState",
      screen_magnifier_enabled);
  base::FundamentalValue virtual_keyboard_enabled(
      pref_service->GetBoolean(prefs::kVirtualKeyboardEnabled));
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.setVirtualKeyboardCheckboxState",
      virtual_keyboard_enabled);
}
#endif

void AdvancedOptionsHandler::SetupMetricsReportingCheckbox() {
#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
  base::FundamentalValue checked(enable_metrics_recording_.GetValue());
  base::FundamentalValue disabled(enable_metrics_recording_.IsManaged());
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.SetMetricsReportingCheckboxState", checked, disabled);
#endif
}

void AdvancedOptionsHandler::SetupMetricsReportingSettingVisibility() {
#if defined(GOOGLE_CHROME_BUILD) && defined(OS_CHROMEOS)
  // Don't show the reporting setting if we are in the guest mode.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession)) {
    base::FundamentalValue visible(false);
    web_ui()->CallJavascriptFunction(
        "AdvancedOptions.SetMetricsReportingSettingVisibility", visible);
  }
#endif
}

void AdvancedOptionsHandler::SetupFontSizeSelector() {
  // We're only interested in integer values, so convert to int.
  base::FundamentalValue font_size(default_font_size_.GetValue());
  web_ui()->CallJavascriptFunction("AdvancedOptions.SetFontSize", font_size);
}

void AdvancedOptionsHandler::SetupPageZoomSelector() {
  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
  double default_zoom_level = pref_service->GetDouble(prefs::kDefaultZoomLevel);
  double default_zoom_factor =
      WebKit::WebView::zoomLevelToZoomFactor(default_zoom_level);

  // Generate a vector of zoom factors from an array of known presets along with
  // the default factor added if necessary.
  std::vector<double> zoom_factors =
      chrome_page_zoom::PresetZoomFactors(default_zoom_factor);

  // Iterate through the zoom factors and and build the contents of the
  // selector that will be sent to the javascript handler.
  // Each item in the list has the following parameters:
  // 1. Title (string).
  // 2. Value (double).
  // 3. Is selected? (bool).
  ListValue zoom_factors_value;
  for (std::vector<double>::const_iterator i = zoom_factors.begin();
       i != zoom_factors.end(); ++i) {
    ListValue* option = new ListValue();
    double factor = *i;
    int percent = static_cast<int>(factor * 100 + 0.5);
    option->Append(Value::CreateStringValue(
        l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, percent)));
    option->Append(Value::CreateDoubleValue(factor));
    bool selected = content::ZoomValuesEqual(factor, default_zoom_factor);
    option->Append(Value::CreateBooleanValue(selected));
    zoom_factors_value.Append(option);
  }

  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.SetupPageZoomSelector", zoom_factors_value);
}

void AdvancedOptionsHandler::SetupAutoOpenFileTypesDisabledAttribute() {
  // Set the enabled state for the AutoOpenFileTypesResetToDefault button.
  // We enable the button if the user has any auto-open file types registered.
  DownloadManager* manager =
      web_ui()->GetWebContents()->GetBrowserContext()->GetDownloadManager();
  bool disabled = !(manager &&
      DownloadPrefs::FromDownloadManager(manager)->IsAutoOpenUsed());
  base::FundamentalValue value(disabled);
  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.SetAutoOpenFileTypesDisabledAttribute", value);
}

void AdvancedOptionsHandler::SetupProxySettingsSection() {
#if !defined(OS_CHROMEOS)
  // Disable the button if proxy settings are managed by a sysadmin or
  // overridden by an extension.
  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
  const PrefService::Preference* proxy_config =
      pref_service->FindPreference(prefs::kProxy);
  bool is_extension_controlled = (proxy_config &&
                                  proxy_config->IsExtensionControlled());

  base::FundamentalValue disabled(proxy_prefs_->IsManaged() ||
                            is_extension_controlled);

  // Get the appropriate info string to describe the button.
  string16 label_str;
  if (is_extension_controlled) {
    label_str = l10n_util::GetStringUTF16(IDS_OPTIONS_EXTENSION_PROXIES_LABEL);
  } else {
    label_str = l10n_util::GetStringFUTF16(IDS_OPTIONS_SYSTEM_PROXIES_LABEL,
        l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
  }
  StringValue label(label_str);

  web_ui()->CallJavascriptFunction(
      "AdvancedOptions.SetupProxySettingsSection", disabled, label);
#endif  // !defined(OS_CHROMEOS)
}

void AdvancedOptionsHandler::SetupSSLConfigSettings() {
  {
    base::FundamentalValue checked(rev_checking_enabled_.GetValue());
    base::FundamentalValue disabled(rev_checking_enabled_.IsManaged());
    web_ui()->CallJavascriptFunction(
        "AdvancedOptions.SetCheckRevocationCheckboxState", checked, disabled);
  }
}

}  // namespace options2


text:
module Cybersourcery
  class CybersourceryError < StandardError; end
end


text:
- Speed up development of things like service discovery and application deployment.
- Being able to _prove_ an PoC with automation.

## How it works
1. VMs or containers are spun up.
2. Scripts or commands are run over SSH.
3. Tests are run.
3. The VMs or containers are killed.

## Backends
- Docker


text:
import {validate as isValidEmail} from "email-validator";
import {generate, verify} from "password-hash";
import {v4} from "uuid";
const ValidatePassword = require("validate-password");

import * as events from "../events";
import {IPasswordChangeRequestedUserState} from "../identities/definitions";
import {IdentitiesStore} from "../identities/store";
import * as defs from "./definitions";
import {validateSyntax} from "./validator";

const now = () => new Date();
const isEmpty = (name: string) => name.trim().length === 0;
const passwordValidator = new ValidatePassword();

export function commandProcessor(identitiesStore: IdentitiesStore): (command: defs.Command) => Promise<events.Event> {
    return command => {
        // TODO  syntax validation of command should already have happened at this point
        const syntaxIssues = validateSyntax(command);
        if (syntaxIssues.length > 0) {
            return Promise.reject(syntaxIssues);
        }

        switch (command.commandType) {
            case "authenticateUser": {
                const user = identitiesStore.userByEmailAddress(command.emailAddress);
                if (user === undefined) {
                    return Promise.reject("email address not known");
                }
                if (user.state.userStateType !== "active") {
                    return Promise.reject("user not active");
                }
                if (!verify(command.password, user.encryptedPassword)) {
                    return Promise.reject("password not valid");
                }
                return Promise.resolve({
                    eventType: "userAuthenticated",
                    timestamp: now(),
                    userId: user.id
                } as events.IUserAuthenticatedEvent);
            }
            case "addIdentityToGroup": {
                const group = identitiesStore.byId(command.groupId);
                const member = identitiesStore.byId(command.memberId);
                if (group === undefined || group.identityType !== "group") {
                    return Promise.reject("group does not exist");
                }
                if (member === undefined) {
                    return Promise.reject("member does not exists");
                }
                if (member.id in group.members) {
                    return Promise.reject("member already in group");
                }
                if (identitiesStore.circularAfterAdding(group.id, member.id)) {
                    return Promise.reject("circularity not permitted");
                }
                return Promise.resolve({
                        eventType: "identityAddedToGroup",
                        timestamp: now(),
                        groupId: command.groupId,
                        memberId: command.memberId
                    } as events.IIdentityAddedToGroupEvent);
            }
            case "confirmEmailAddress": {
                const user = identitiesStore.userByEmailAddress(command.emailAddress);
                if (user === undefined) {
                    return Promise.reject("email address not known");
                }
                if (user.state.userStateType !== "confirmEmailAddress") {
                    return Promise.reject("state not valid for this command");
                } else {
                    if (user.state.token !== command.token) {
                        return Promise.reject("token not valid");
                    }
                }
                return Promise.resolve({
                    eventType: "emailAddressConfirmed",
                    timestamp: now(),
                    userId: user.id
                } as events.IEmailAddressConfirmedEvent);
            }
            case "createGroup": {
                return isEmpty(command.name)
                    ? Promise.reject("name not valid")
                    : Promise.resolve({
                            eventType: "groupCreated",
                            id: v4(),
                            timestamp: now(),
                            name: command.name
                        } as events.IGroupCreatedEvent)
                    ;
            }
            case "createUser": {
                if (identitiesStore.userByEmailAddress(command.emailAddress) !== undefined) {
                    return Promise.reject("user with that email address already exists");
                }
                if (isEmpty(command.name)) {
                    return Promise.reject("name empty");
                }
                if (isEmpty(command.password)) {
                    return Promise.reject("password empty");
                }
                const passwordData = passwordValidator.checkPassword(command.password);
                if (passwordData.isValid) {
                    return Promise.reject("password not valid: " +  passwordData.validationMessage);
                }
                if (!isValidEmail(command.emailAddress)) {
                    return Promise.reject("email address not valid");
                }
                return Promise.resolve({
                        eventType: "userCreated",
                        id: v4(),
                        timestamp: now(),
                        name: command.name,
                        emailAddress: command.emailAddress,
                        encryptedPassword: generate(command.password)
                    } as events.IUserCreatedEvent);
            }
            case "requestPasswordChange": {
                const user = identitiesStore.userByEmailAddress(command.emailAddress);
                if (user === undefined) {
                    return Promise.reject("email address not known");
                }
                if (user.state.userStateType !== "active") {
                    return Promise.reject("user not active");
                }
                return Promise.resolve({
                        eventType: "identityUpdated",
                        timestamp: now(),
                        id: user.id,
                        data: {
                            state: {
                                userStateType: "passwordChangeRequested",
                                token: v4()
                            } as IPasswordChangeRequestedUserState
                        }
                    } as events.IIdentityUpdatedEvent);
            }
            /*
            * Note: if TypeScript insists on a default case, you've missed a case!
            * Conversely, `command` should have type `never` in the default case.
            */
        }
    };
}