def pcsd_restart_nodes(session, nodes)
  node_response = {}
  threads = []
  nodes.each { |node|
    threads << Thread.new {
      code, response = send_request_with_token(
        session, node, '/pcsd_restart', true
      )
      node_response[node] = [code, response]
    }
  }
  threads.each { |t| t.join }

  node_error = []
  node_status = {}
  node_response.each { |node, response|
    if response[0] == 200
      node_status[node] = {
        'status' => 'ok',
        'text' => 'Success',
      }
    else
      text = response[1]
      if response[0] == 401
        text = "Unable to authenticate, try running 'pcs cluster auth'"
      elsif response[0] == 400
        begin
          parsed_response = JSON.parse(response[1], {:symbolize_names => true})
          if parsed_response[:noresponse]
            text = "Unable to connect"
          elsif parsed_response[:notoken] or parsed_response[:notauthorized]
            text = "Unable to authenticate, try running 'pcs cluster auth'"
          end
        rescue JSON::ParserError
        end
      end
      node_status[node] = {
        'status' => 'error',
        'text' => text
      }
      node_error << node
    end
  }
  return {
    'status' => node_error.empty?() ? 'ok' : 'error',
    'text' => node_error.empty?() ? 'Success' : \
      "Unable to restart pcsd on nodes: #{node_error.join(', ')}",
    'node_status' => node_status,
  }
end


def add_acl_remote(params, request, session)
  if not allowed_for_local_cluster(session, Permissions::GRANT)
    return 403, 'Permission denied'
  end
  if params["item"] == "permission"
    retval = add_acl_permission(
      session,
      params["role_id"], params["type"], params["xpath_id"], params["query_id"]
    )
  elsif (params["item"] == "user") or (params["item"] == "group")
    retval = add_acl_usergroup(
      session, params["role_id"], params["item"], params["usergroup"]
    )
  else
    retval = "Error: Unknown adding request"
  end

  if retval == ""
    return [200, "Successfully added permission to role"]
  else
    return [
      400,
      retval.include?("cib_replace failed") ? "Error adding permission" : retval
    ]
  end
end


def node_restart(params, request, session)
  if params[:name]
    code, response = send_request_with_token(
      session, params[:name], 'node_restart', true
    )
  else
    if not allowed_for_local_cluster(session, Permissions::WRITE)
      return 403, 'Permission denied'
    end
    $logger.info "Restarting Node"
    output =  `/sbin/reboot`
    $logger.debug output
    return output
  end
end


def node_standby(params, request, session)
  if params[:name]
    code, response = send_request_with_token(
      session, params[:name], 'node_standby', true, {"node"=>params[:name]}
    )
    # data={"node"=>params[:name]} for backward compatibility with older versions of pcs/pcsd
  else
    if not allowed_for_local_cluster(session, Permissions::WRITE)
      return 403, 'Permission denied'
    end
    $logger.info "Standby Node"
    stdout, stderr, retval = run_cmd(session, PCS, "cluster", "standby")
    return stdout
  end
end


def check_gui_status_of_nodes(session, nodes, check_mutuality=false, timeout=10)
  options = {}
  options[:check_auth_only] = '' if not check_mutuality
  threads = []
  not_authorized_nodes = []
  online_nodes = []
  offline_nodes = []

  nodes = nodes.uniq.sort
  nodes.each { |node|
    threads << Thread.new {
      code, response = send_request_with_token(
        session, node, 'check_auth', false, options, true, nil, timeout
      )
      if code == 200
        if check_mutuality
          begin
            parsed_response = JSON.parse(response)
            if parsed_response['node_list'] and parsed_response['node_list'].uniq.sort == nodes
              online_nodes << node
            else
              not_authorized_nodes << node
            end
          rescue
            not_authorized_nodes << node
          end
        else
          online_nodes << node
        end
      else
        begin
          parsed_response = JSON.parse(response)
          if parsed_response['notauthorized'] or parsed_response['notoken']
            not_authorized_nodes << node
          else
            offline_nodes << node
          end
        rescue JSON::ParserError
        end
      end
    }
  }
  threads.each { |t| t.join }
  return online_nodes, offline_nodes, not_authorized_nodes
end


def cluster_enable(params, request, session)
  if params[:name]
    code, response = send_request_with_token(
      session, params[:name], 'cluster_enable', true
    )
  else
    if not allowed_for_local_cluster(session, Permissions::WRITE)
      return 403, 'Permission denied'
    end
    success = enable_cluster(session)
    if not success
      return JSON.generate({"error" => "true"})
    end
    return "Cluster Enabled"
  end
end


def resource_metadata(params, request, session)
  if not allowed_for_local_cluster(session, Permissions::READ)
    return 403, 'Permission denied'
  end
  return 200 if not params[:resourcename] or params[:resourcename] == ""
  resource_name = params[:resourcename][params[:resourcename].rindex(':')+1..-1]
  class_provider = params[:resourcename][0,params[:resourcename].rindex(':')]

  @resource = ResourceAgent.new(params[:resourcename])
  if class_provider == "ocf:heartbeat"
    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, HEARTBEAT_AGENTS_DIR + resource_name)
  elsif class_provider == "ocf:pacemaker"
    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, PACEMAKER_AGENTS_DIR + resource_name)
  elsif class_provider == 'nagios'
    @resource.required_options, @resource.optional_options, @resource.info = getResourceMetadata(session, NAGIOS_METADATA_DIR + resource_name + '.xml')
  end
  @new_resource = params[:new]
  @resources, @groups = getResourcesGroups(session)

  erb :resourceagentform
end


  def testLoginByToken
    users = []
    users << {"username" => "user1", "token" => "token1"}
    users << {"username" => "user2", "token" => "token2"}
    users << {"username" => SUPERUSER, "token" => "tokenS"}
    password_file = File.open($user_pass_file, File::RDWR|File::CREAT)
    password_file.truncate(0)
    password_file.rewind
    password_file.write(JSON.pretty_generate(users))
    password_file.close()

    session = {}
    cookies = {}
    result = PCSAuth.loginByToken(session, cookies)
    assert_equal(false, result)
    assert_equal({}, session)

    session = {}
    cookies = {'token' => 'tokenX'}
    result = PCSAuth.loginByToken(session, cookies)
    assert_equal(false, result)
    assert_equal({}, session)

    session = {}
    cookies = {'token' => 'token1'}
    result = PCSAuth.loginByToken(session, cookies)
    assert_equal(true, result)
    assert_equal(
      {:username => 'user1', :usergroups => ['group1', 'haclient']},
      session
    )

    session = {}
    cookies = {
      'token' => 'token1',
      'CIB_user' => 'userX',
      'CIB_user_groups' => PCSAuth.cookieUserEncode('groupX')
    }
    result = PCSAuth.loginByToken(session, cookies)
    assert_equal(true, result)
    assert_equal(
      {:username => 'user1', :usergroups => ['group1', 'haclient']},
      session
    )

    session = {}
    cookies = {'token' => 'tokenS'}
    result = PCSAuth.loginByToken(session, cookies)
    assert_equal(true, result)
    assert_equal(
      {:username => SUPERUSER, :usergroups => []},
      session
    )

    session = {}
    cookies = {
      'token' => 'tokenS',
      'CIB_user' => 'userX',
      'CIB_user_groups' => PCSAuth.cookieUserEncode('groupX')
    }
    result = PCSAuth.loginByToken(session, cookies)
    assert_equal(true, result)
    assert_equal(
      {:username => 'userX', :usergroups => ['groupX']},
      session
    )
  end


def remove_resource(params, request, session)
  if not allowed_for_local_cluster(session, Permissions::WRITE)
    return 403, 'Permission denied'
  end
  force = params['force']
  no_error_if_not_exists = params.include?('no_error_if_not_exists')
  errors = ""
  params.each { |k,v|
    if k.index("resid-") == 0
      resid = k.gsub('resid-', '')
      command = [PCS, 'resource', 'delete', resid]
      command << '--force' if force
      out, errout, retval = run_cmd(session, *command)
      if retval != 0
        unless out.index(" does not exist.") != -1 and no_error_if_not_exists
          errors += errout.join(' ').strip + "\n"
        end
      end
    end
  }
  errors.strip!
  if errors == ""
    return 200
  else
    $logger.info("Remove resource errors:\n"+errors)
    return [400, errors]
  end
end


def remove_acl_roles_remote(params, request, session)
  if not allowed_for_local_cluster(session, Permissions::GRANT)
    return 403, 'Permission denied'
  end
  errors = ""
  params.each { |name, value|
    if name.index("role-") == 0
      out, errout, retval = run_cmd(
        session, PCS, "acl", "role", "delete", value.to_s, "--autodelete"
      )
      if retval != 0
        errors += "Unable to remove role #{value}"
        unless errout.include?("cib_replace failure")
          errors += ": #{errout.join(" ").strip()}"
        end
        errors += "\n"
        $logger.info errors
      end
    end
  }
  if errors == ""
    return [200, "Successfully removed ACL roles"]
  else
    return [400, errors]
  end
end


def get_tokens(params, request, session)
  # pcsd runs as root thus always returns hacluster's tokens
  if not allowed_for_local_cluster(session, Permissions::FULL)
    return 403, 'Permission denied'
  end
  return [200, JSON.generate(read_tokens)]
end


  def self.save_sync_new_tokens(config, new_tokens, nodes, cluster_name)
    with_new_tokens = PCSTokens.new(config.text)
    with_new_tokens.tokens.update(new_tokens)
    config_new = PcsdTokens.from_text(with_new_tokens.text)
    if not cluster_name or cluster_name.empty?
      # we run on a standalone host, no config syncing
      config_new.version += 1
      config_new.save()
      return true, {}
    end
    # we run in a cluster so we need to sync the config
    publisher = ConfigPublisher.new(
      PCSAuth.getSuperuserSession(), [config_new], nodes, cluster_name,
      new_tokens
    )
    old_configs, node_responses = publisher.publish()
    if not old_configs.include?(config_new.class.name)
      # no node had newer tokens file, we are ok, everything done
      return true, node_responses
    end
    # get tokens from all nodes and merge them
    fetcher = ConfigFetcher.new(
      PCSAuth.getSuperuserSession(), [config_new.class], nodes, cluster_name
    )
    fetched_tokens = fetcher.fetch_all()[config_new.class.name]
    config_new = Cfgsync::merge_tokens_files(config, fetched_tokens, new_tokens)
    # and try to publish again
    return Cfgsync::save_sync_new_version(
      config_new, nodes, cluster_name, true, new_tokens
    )
  end


def set_sync_options(params, request, session)
  if not allowed_for_local_cluster(session, Permissions::FULL)
    return 403, 'Permission denied'
  end

  options = [
    'sync_thread_pause', 'sync_thread_resume',
    'sync_thread_disable', 'sync_thread_enable',
  ]
  if params.keys.count { |key| options.include?(key) } != 1
    return [400, 'Exactly one option has to be specified']
  end

  if params['sync_thread_disable']
    if Cfgsync::ConfigSyncControl.sync_thread_disable($semaphore_cfgsync)
      return 'sync thread disabled'
    else
      return [400, 'sync thread disable error']
    end
  end

  if params['sync_thread_enable']
    if Cfgsync::ConfigSyncControl.sync_thread_enable()
      return 'sync thread enabled'
    else
      return [400, 'sync thread enable error']
    end
  end

  if params['sync_thread_resume']
    if Cfgsync::ConfigSyncControl.sync_thread_resume()
      return 'sync thread resumed'
    else
      return [400, 'sync thread resume error']
    end
  end

  if params['sync_thread_pause']
    if Cfgsync::ConfigSyncControl.sync_thread_pause(
        $semaphore_cfgsync, params['sync_thread_pause']
      )
      return 'sync thread paused'
    else
      return [400, 'sync thread pause error']
    end
  end

  return [400, 'Exactly one option has to be specified']
end


  def protected!
    gui_request = ( # these are URLs for web pages
      request.path == '/' or
      request.path == '/manage' or
      request.path == '/permissions' or
      request.path.match('/managec/.+/main')
    )
    if request.path.start_with?('/remote/') or request.path == '/run_pcs'
      @auth_user = PCSAuth.loginByToken(cookies)
      unless @auth_user
        halt [401, '{"notauthorized":"true"}']
      end
    else #/managec/* /manage/* /permissions
      if !gui_request and
        request.env['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest'
      then
        # Accept non GUI requests only with header
        # "X_REQUESTED_WITH: XMLHttpRequest". (check if they are send via AJAX).
        # This prevents CSRF attack.
        halt [401, '{"notauthorized":"true"}']
      elsif not PCSAuth.isLoggedIn(session)
        if gui_request
          session[:pre_login_path] = request.path
          redirect '/login'
        else
          halt [401, '{"notauthorized":"true"}']
        end
      end
    end
  end


def set_cluster_conf(params, request, session)
  if not allowed_for_local_cluster(session, Permissions::FULL)
    return 403, 'Permission denied'
  end
  if params[:cluster_conf] != nil and params[:cluster_conf].strip != ""
    Cfgsync::ClusterConf.backup()
    Cfgsync::ClusterConf.from_text(params[:cluster_conf]).save()
    return 200, 'Updated cluster.conf...'
  else
    $logger.info "Invalid cluster.conf file"
    return 400, 'Failed to update cluster.conf...'
  end
end


def verify_cert_key_pair(cert, key)
  errors = []
  cert_modulus = nil
  key_modulus = nil

  stdout, stderr, retval = run_cmd_options(
    PCSAuth.getSuperuserSession(),
    {
      'stdin' => cert,
    },
    '/usr/bin/openssl', 'x509', '-modulus', '-noout'
  )
  if retval != 0
    errors << "Invalid certificate: #{stderr.join}"
  else
    cert_modulus = stdout.join.strip
  end

  stdout, stderr, retval = run_cmd_options(
    PCSAuth.getSuperuserSession(),
    {
      'stdin' => key,
    },
    '/usr/bin/openssl', 'rsa', '-modulus', '-noout'
  )
  if retval != 0
    errors << "Invalid key: #{stderr.join}"
  else
    key_modulus = stdout.join.strip
  end

  if errors.empty? and cert_modulus and key_modulus
    if cert_modulus != key_modulus
      errors << 'Certificate does not match the key'
    end
  end

  return errors
end


def add_colocation_constraint(
  session, resourceA, resourceB, score, force=false, autocorrect=true
)
  if score == "" or score == nil
    score = "INFINITY"
  end
  command = [
    PCS, "constraint", "colocation", "add", resourceA, resourceB, score
  ]
  command << '--force' if force
  command << '--autocorrect' if autocorrect
  stdout, stderr, retval = run_cmd(session, *command)
  return retval, stderr.join(' ')
end


def get_pacemaker_version()
  begin
    stdout, stderror, retval = run_cmd(
      PCSAuth.getSuperuserSession, PACEMAKERD, "-$"
    )
  rescue
    stdout = []
  end
  if retval == 0
    match = /(\d+)\.(\d+)\.(\d+)/.match(stdout.join())
    if match
      return match[1..3].collect { | x | x.to_i }
    end
  end
  return nil
end


  def protected!
    if not PCSAuth.loginByToken(session, cookies) and not PCSAuth.isLoggedIn(session)
      # If we're on /managec/<cluster_name>/main we redirect
      match_expr = "/managec/(.*)/(.*)"
      mymatch = request.path.match(match_expr)
      on_managec_main = false
      if mymatch and mymatch.length >= 3 and mymatch[2] == "main"
        on_managec_main = true
      end

      if request.path.start_with?('/remote') or
        (request.path.match(match_expr) and not on_managec_main) or
        '/run_pcs' == request.path or
        '/clusters_overview' == request.path or
        request.path.start_with?('/permissions_')
      then
        $logger.info "ERROR: Request without authentication"
        halt [401, '{"notauthorized":"true"}']
      else
        session[:pre_login_path] = request.path
        redirect '/login'
      end
    end
  end


def remove_constraint(session, constraint_id)
  stdout, stderror, retval = run_cmd(
    session, PCS, "constraint", "remove", constraint_id
  )
  $logger.info stdout
  return retval
end


def get_resource_agents_avail(session)
  code, result = send_cluster_request_with_token(
    session, params[:cluster], 'get_avail_resource_agents'
  )
  return {} if 200 != code
  begin
    ra = JSON.parse(result)
    if (ra["noresponse"] == true) or (ra["notauthorized"] == "true") or (ra["notoken"] == true) or (ra["pacemaker_not_running"] == true)
      return {}
    else
      return ra
    end
  rescue JSON::ParserError
    return {}
  end
end


def node_unstandby(params, request, session)
  if params[:name]
    code, response = send_request_with_token(
      session, params[:name], 'node_unstandby', true, {"node"=>params[:name]}
    )
    # data={"node"=>params[:name]} for backward compatibility with older versions of pcs/pcsd
  else
    if not allowed_for_local_cluster(session, Permissions::WRITE)
      return 403, 'Permission denied'
    end
    $logger.info "Unstandby Node"
    stdout, stderr, retval = run_cmd(session, PCS, "cluster", "unstandby")
    return stdout
  end
end


  def self.getUsersGroups(username)
    stdout, stderr, retval = run_cmd(
      getSuperuserSession, "id", "-Gn", username
    )
    if retval != 0
      $logger.info(
        "Unable to determine groups of user '#{username}': #{stderr.join(' ').strip}"
      )
      return [false, []]
    end
    return [true, stdout.join(' ').split(nil)]
  end


function cleaner() {
  setTimeout(() => {
    if (ids.length < 1) { return; }
    ActiveRooms.forEach((element, index) => {
      element.Players.forEach((element2, index2) => {
        if (!ids.includes(element2.Id)) {
          ActiveRooms[index].Players.splice(index2, 1);
          ActiveRooms[index].Players.forEach((element3) => {
            element3.socket.emit('UserLeave', JSON.stringify({ RoomId: element.Id, UserLeaved: element2.Id }));
          });
        }
      });
    });
    ActiveRooms.forEach((element, index) => {
      if (element.Players.length === 0) {
        ActiveRooms.splice(index, 1);
      }
    });
    // ids = [];
  }, 5000);
}


export async function execRequest(routes: Routers, ctx: AppContext) {
	const match = findMatchingRoute(ctx.path, routes);
	if (!match) throw new ErrorNotFound();

	const endPoint = match.route.findEndPoint(ctx.request.method as HttpMethod, match.subPath.schema);
	if (ctx.URL && !isValidOrigin(ctx.URL.origin, baseUrl(endPoint.type), endPoint.type)) throw new ErrorNotFound(`Invalid origin: ${ctx.URL.origin}`, 'invalidOrigin');

	// This is a generic catch-all for all private end points - if we
	// couldn't get a valid session, we exit now. Individual end points
	// might have additional permission checks depending on the action.
	if (!match.route.isPublic(match.subPath.schema) && !ctx.joplin.owner) throw new ErrorForbidden();

	return endPoint.handler(match.subPath, ctx);
}


function RegisterUserName(socket, Data) {
  var userName = Data.UserName.split('>').join(' ').split('<').join(' ').split('/').join(' ');
  if (userName.length > 16) userName = userName.slice(0, 16);
  if (userName.toLowerCase().includes('você')) userName = '~' + userName;
  ActiveRooms.forEach((element, index) => {
    if (element.Id === Data.RoomId) {
      ActiveRooms[index].Players.forEach((element2, index2) => {
        if (element2.Name.toLowerCase() === userName.toLowerCase()) {
          console.log('UserName already exists');
          return socket.emit('UserNameAlreadyExists');
        } else if (element2.Id === Data.UserId) {
          ActiveRooms[index].Players[index2].Name = userName;
        }
        socket.emit('UserNameInformation', JSON.stringify({ RoomId: element.Id, UserId: element2.Id, NickName: element2.Name }));
      });
    }
  });
}


		triggerMassAction: function (massActionUrl, type) {
			const self = this.relatedListInstance;
			let validationResult = self.checkListRecordSelected();
			if (validationResult != true) {
				let progressIndicatorElement = $.progressIndicator(),
					selectedIds = self.readSelectedIds(true),
					excludedIds = self.readExcludedIds(true),
					cvId = self.getCurrentCvId(),
					postData = self.getCompleteParams();
				delete postData.mode;
				delete postData.view;
				postData.viewname = cvId;
				postData.selected_ids = selectedIds;
				postData.excluded_ids = excludedIds;
				let actionParams = {
					type: 'POST',
					url: massActionUrl,
					data: postData
				};
				if (type === 'sendByForm') {
					app.openUrlMethodPost(massActionUrl, postData);
					progressIndicatorElement.progressIndicator({ mode: 'hide' });
				} else {
					AppConnector.request(actionParams)
						.done(function (responseData) {
							progressIndicatorElement.progressIndicator({ mode: 'hide' });
							if (responseData && responseData.result !== null) {
								if (responseData.result.notify) {
									Vtiger_Helper_Js.showMessage(responseData.result.notify);
								}
								if (responseData.result.reloadList) {
									Vtiger_Detail_Js.reloadRelatedList();
								}
								if (responseData.result.processStop) {
									progressIndicatorElement.progressIndicator({ mode: 'hide' });
									return false;
								}
							}
						})
						.fail(function (error, err) {
							progressIndicatorElement.progressIndicator({ mode: 'hide' });
						});
				}
			} else {
				self.noRecordSelectedAlert();
			}
		},