<commit_before>package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/securecookie"
	"gopkg.in/authboss.v0"
)

var cookieStore *securecookie.SecureCookie

type CookieStorer struct {
	w http.ResponseWriter
	r *http.Request
}

func NewCookieStorer(w http.ResponseWriter, r *http.Request) authboss.ClientStorer {
	return &CookieStorer{w, r}
}

func (s CookieStorer) Get(key string) (string, bool) {
	cookie, err := s.r.Cookie(key)
	if err != nil {
		return "", false
	}

	var value string
	err = cookieStore.Decode(key, cookie.Value, &value)
	if err != nil {
		return "", false
	}

	return value, true
}

func (s CookieStorer) Put(key, value string) {
	encoded, err := cookieStore.Encode(key, value)
	if err != nil {
		fmt.Println(err)
	}

	cookie := &http.Cookie{
		Expires: time.Now().UTC().AddDate(1, 0, 0),
		Name:    key,
		Value:   encoded,
		Path:    "/",
	}
	http.SetCookie(s.w, cookie)
}

func (s CookieStorer) Del(key string) {
	cookie := &http.Cookie{
		MaxAge: -1,
		Name:   key,
		Path:   "/",
	}
	http.SetCookie(s.w, cookie)
}
<commit_msg>update imports<commit_after>package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/securecookie"
	"gopkg.in/authboss.v1"
)

var cookieStore *securecookie.SecureCookie

type CookieStorer struct {
	w http.ResponseWriter
	r *http.Request
}

func NewCookieStorer(w http.ResponseWriter, r *http.Request) authboss.ClientStorer {
	return &CookieStorer{w, r}
}

func (s CookieStorer) Get(key string) (string, bool) {
	cookie, err := s.r.Cookie(key)
	if err != nil {
		return "", false
	}

	var value string
	err = cookieStore.Decode(key, cookie.Value, &value)
	if err != nil {
		return "", false
	}

	return value, true
}

func (s CookieStorer) Put(key, value string) {
	encoded, err := cookieStore.Encode(key, value)
	if err != nil {
		fmt.Println(err)
	}

	cookie := &http.Cookie{
		Expires: time.Now().UTC().AddDate(1, 0, 0),
		Name:    key,
		Value:   encoded,
		Path:    "/",
	}
	http.SetCookie(s.w, cookie)
}

func (s CookieStorer) Del(key string) {
	cookie := &http.Cookie{
		MaxAge: -1,
		Name:   key,
		Path:   "/",
	}
	http.SetCookie(s.w, cookie)
}
<|endoftext|>


<commit_before>// Copyright 2011 Arne Roomann-Kurrik.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oauth1a

import (
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

func LoadCredentials() (string, string, string, string, error) {
	credentials, err := ioutil.ReadFile("CREDENTIALS")
	if err != nil {
		return "", "", "", "", err
	}
	lines := strings.Split(string(credentials), "\n")
	return lines[0], lines[1], lines[2], lines[3], nil
}

func GetTwitterConfig(t *testing.T) (*Service, *UserConfig) {
	key, secret, token, token_secret, err := LoadCredentials()
	if err != nil {
		t.Fatal("Could not load CREDENTIALS file")
	}
	service := &Service{
		RequestURL:   "https://api.twitter.com/oauth/request_token",
		AuthorizeURL: "https://api.twitter.com/oauth/request_token",
		AccessURL:    "https://api.twitter.com/oauth/request_token",
		ClientConfig: &ClientConfig{
			ConsumerKey:    key,
			ConsumerSecret: secret,
			CallbackURL:    "oob",
		},
		Signer: new(HmacSha1Signer),
	}
	user := NewAuthorizedConfig(token, token_secret)
	return service, user
}

func TestIntegration(t *testing.T) {
	if testing.Short() == true {
		t.Log("Not running integration test because short was specified.")
		return
	}
	service, userConfig := GetTwitterConfig(t)
	httpClient := new(http.Client)
	url := "https://api.twitter.com/1/account/verify_credentials.json"
	httpRequest, _ := http.NewRequest("GET", url, nil)
	service.Sign(httpRequest, userConfig)
	httpResponse, err := httpClient.Do(httpRequest)
	if err != nil {
		t.Error("Response had an error")
	}
	if httpResponse.StatusCode != 200 {
		t.Errorf("Response returned code of %v", httpResponse.StatusCode)
	}
}
<commit_msg>Adds a failing GetRequestToken test.<commit_after>// Copyright 2011 Arne Roomann-Kurrik.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oauth1a

import (
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

func LoadCredentials() (string, string, string, string, error) {
	credentials, err := ioutil.ReadFile("CREDENTIALS")
	if err != nil {
		return "", "", "", "", err
	}
	lines := strings.Split(string(credentials), "\n")
	return lines[0], lines[1], lines[2], lines[3], nil
}

func GetTwitterConfig(t *testing.T) (*Service, *UserConfig) {
	key, secret, token, token_secret, err := LoadCredentials()
	if err != nil {
		t.Fatal("Could not load CREDENTIALS file")
	}
	service := &Service{
		RequestURL:   "https://api.twitter.com/oauth/request_token",
		AuthorizeURL: "https://api.twitter.com/oauth/request_token",
		AccessURL:    "https://api.twitter.com/oauth/request_token",
		ClientConfig: &ClientConfig{
			ConsumerKey:    key,
			ConsumerSecret: secret,
			CallbackURL:    "oob",
		},
		Signer: new(HmacSha1Signer),
	}
	user := NewAuthorizedConfig(token, token_secret)
	return service, user
}

func TestIntegration(t *testing.T) {
	if testing.Short() == true {
		t.Log("Not running integration test because short was specified.")
		return
	}
	service, userConfig := GetTwitterConfig(t)
	httpClient := new(http.Client)
	url := "https://api.twitter.com/1/account/verify_credentials.json"
	httpRequest, _ := http.NewRequest("GET", url, nil)
	service.Sign(httpRequest, userConfig)
	httpResponse, err := httpClient.Do(httpRequest)
	if err != nil {
		t.Error("Response had an error")
	}
	if httpResponse.StatusCode != 200 {
		t.Errorf("Response returned code of %v", httpResponse.StatusCode)
	}
}

func TestGetRequestToken(t *testing.T) {
	if testing.Short() == true {
		t.Log("Not running integration test because short was specified.")
		return
	}
	service, userConfig := GetTwitterConfig(t)
	httpClient := new(http.Client)
	err := userConfig.GetRequestToken(service, httpClient)
	if err != nil {
		t.Errorf("Response had an error: %v", err)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"cf"
	"cf/configuration"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

type ApplicationRepository interface {
	FindByName(config *configuration.Configuration, name string) (app cf.Application, err error)
	SetEnv(config *configuration.Configuration, app cf.Application, name string, value string) (err error)
}

type CloudControllerApplicationRepository struct {
}

func (repo CloudControllerApplicationRepository) FindByName(config *configuration.Configuration, name string) (app cf.Application, err error) {
	apps, err := findApplications(config)
	lowerName := strings.ToLower(name)

	if err != nil {
		return
	}

	for _, a := range apps {
		if strings.ToLower(a.Name) == lowerName {
			return a, nil
		}
	}

	err = errors.New("Application not found")
	return
}

func (repo CloudControllerApplicationRepository) SetEnv(config *configuration.Configuration, app cf.Application, name string, value string) (err error) {
	path := fmt.Sprintf("%s/v2/apps/%s", config.Target, app.Guid)
	data := fmt.Sprintf(`{"environment_json":{"%s":"%s"}}`, name, value)
	request, err := http.NewRequest("PUT", path, strings.NewReader(data))
	request.Header.Set("Authorization", config.AccessToken)

	if err != nil {
		return
	}

	err = PerformRequest(request)
	return
}

func findApplications(config *configuration.Configuration) (apps []cf.Application, err error) {
	path := fmt.Sprintf("%s/v2/spaces/%s/apps", config.Target, config.Space.Guid)
	request, err := http.NewRequest("GET", path, nil)
	if err != nil {
		return
	}
	request.Header.Set("Authorization", config.AccessToken)

	response := new(ApiResponse)

	err = PerformRequestForBody(request, response)

	if err != nil {
		return
	}

	for _, r := range response.Resources {
		apps = append(apps, cf.Application{r.Entity.Name, r.Metadata.Guid})
	}

	return
}
<commit_msg>Handle errors when creating request<commit_after>package api

import (
	"cf"
	"cf/configuration"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

type ApplicationRepository interface {
	FindByName(config *configuration.Configuration, name string) (app cf.Application, err error)
	SetEnv(config *configuration.Configuration, app cf.Application, name string, value string) (err error)
}

type CloudControllerApplicationRepository struct {
}

func (repo CloudControllerApplicationRepository) FindByName(config *configuration.Configuration, name string) (app cf.Application, err error) {
	apps, err := findApplications(config)
	lowerName := strings.ToLower(name)

	if err != nil {
		return
	}

	for _, a := range apps {
		if strings.ToLower(a.Name) == lowerName {
			return a, nil
		}
	}

	err = errors.New("Application not found")
	return
}

func (repo CloudControllerApplicationRepository) SetEnv(config *configuration.Configuration, app cf.Application, name string, value string) (err error) {
	path := fmt.Sprintf("%s/v2/apps/%s", config.Target, app.Guid)
	data := fmt.Sprintf(`{"environment_json":{"%s":"%s"}}`, name, value)
	request, err := http.NewRequest("PUT", path, strings.NewReader(data))
	if err != nil {
		return
	}
	request.Header.Set("Authorization", config.AccessToken)

	if err != nil {
		return
	}

	err = PerformRequest(request)
	return
}

func findApplications(config *configuration.Configuration) (apps []cf.Application, err error) {
	path := fmt.Sprintf("%s/v2/spaces/%s/apps", config.Target, config.Space.Guid)
	request, err := http.NewRequest("GET", path, nil)
	if err != nil {
		return
	}
	request.Header.Set("Authorization", config.AccessToken)

	response := new(ApiResponse)

	err = PerformRequestForBody(request, response)

	if err != nil {
		return
	}

	for _, r := range response.Resources {
		apps = append(apps, cf.Application{r.Entity.Name, r.Metadata.Guid})
	}

	return
}
<|endoftext|>


<commit_before>// DO NOT EDIT! (AUTO-GENERATED)

package main

var genDocString = `
lf is a terminal file manager.

Source code can be found in the repository at
https://github.com/gokcehan/lf.

This documentation can either be read from terminal using "lf -doc" or
online at https://godoc.org/github.com/gokcehan/lf.

Reference

The following commands are provided by lf with default keybindings:

    up                (default "k" and "<up>")
    half-up           (default "<c-u>")
    page-up           (default "<c-b>")
    down              (default "j" and "<down>")
    half-down         (default "<c-d>")
    page-down         (default "<c-f>")
    updir             (default "h" and "<left>")
    open              (default "l" and "<right>")
    quit              (default "q")
    bot               (default "G")
    top               (default "gg")
    read              (default ":")
    read-shell        (default "$")
    read-shell-wait   (default "!")
    read-shell-async  (default "&")
    search            (default "/")
    search-back       (default "?")
    search-next       (default "n")
    search-prev       (default "N")
    toggle            (default "<space>")
    invert            (default "v")
    yank              (default "y")
    clear             (default "c")
    delete            (default "d")
    put               (default "p")
    renew             (default "<c-l>")

The following commands are provided by lf without default keybindings:

    sync  synchronizes yanked/deleted files with server
    echo  prints its arguments to the message line
    cd    changes working directory to its argument
    push  simulate key pushes given in its argument

The following command line commands are provided by lf with default
keybindings:

    cmd-escape        (default "<esc>")
    cmd-comp          (default "<tab>")
    cmd-enter         (default "<c-j>" and "<enter>")
    cmd-hist-next     (default "<c-n>")
    cmd-hist-prev     (default "<c-p>")
    cmd-delete        (default "<c-d>" and "<delete>")
    cmd-delete-back   (default "<bs>" and "<bs2>")
    cmd-left          (default "<c-b>" and "<left>")
    cmd-right         (default "<c-f>" and "<right>")
    cmd-beg           (default "<c-a>" and "<home>")
    cmd-end           (default "<c-e>" and "<end>")
    cmd-delete-end    (default "<c-k>")
    cmd-delete-beg    (default "<c-u>")
    cmd-delete-word   (default "<c-w>")
    cmd-put           (default "<c-y>")
    cmd-transpose     (default "<c-t>")

The following options can be used to customize the behavior of lf:

    dircounts   boolean  (default off)
    dirfirst    boolean  (default on)
    globsearch  boolean  (default off)
    hidden      boolean  (default off)
    ignorecase  boolean  (default off)
    preview     boolean  (default on)
    reverse     boolean  (default off)
    smartcase   boolean  (default off)
    wrapscan    boolean  (default on)
    scrolloff   integer  (default 0)
    tabstop     integer  (default 8)
    filesep     string   (default "\n")
    ifs         string   (default "") (not exported if empty)
    previewer   string   (default "") (not filtered if empty)
    shell       string   (default "/bin/sh")
    sortby      string   (default "natural")
    timefmt     string   (default "Mon Jan _2 15:04:05 2006")
    ratios      string   (default "1:2:3")
    info        string   (default "")

The following variables are exported for shell commands:

    $f   current file
    $fs  marked file(s) separated with 'filesep'
    $fx  current file or marked file(s) if any
    $id  id number of the client

Configuration

The configuration file should be located at:

    $XDG_CONFIG_HOME/lf/lfrc"

If "$XDG_CONFIG_HOME" is not set, it defaults to "$HOME/.config" so the
location should be:

    ~/.config/lf/lfrc

A sample configuration file can be found at
https://github.com/gokcehan/lf/blob/master/etc/lfrc.example.

Prefixes

The following command prefixes are used by lf:

    :  read (default)    builtin/custom command
    $  read-shell        shell command
    !  read-shell-wait   shell command waiting for key press
    &  read-shell-async  asynchronous shell command
    /  search            search file in current directory
    ?  search-back       search file in the reverse order

The same evaluator is used for the command line and the configuration file.
The difference is that prefixes are not necessary in the command line.
Instead different modes are provided to read corresponding commands. Note
that by default these modes are mapped to the prefix keys above.

Syntax

Characters from "#" to "\n" are comments and ignored:

    # comments start with '#'

There are three special commands ("set", "map", and "cmd") and their
variants for configuration.

"set" is used to set an option which can be boolean, integer, or string:

    set hidden         # boolean on
    set nohidden       # boolean off
    set hidden!        # boolean toggle
    set scrolloff 10   # integer value
    set sortby time    # string value w/o quotes
    set sortby 'time'  # string value with single quotes (whitespaces)
    set sortby "time"  # string value with double quotes (backslash escapes)

"map" is used to bind a key to a command which can be builtin command,
custom command, or shell command:

    map gh cd ~        # builtin command
    map D trash        # custom command
    map i $less "$f"   # shell command
    map u !du -h .     # waiting shell command

"cmap" is used to bind a key to a command line command which can only be one
of the builtin commands:

    cmap <c-g> cmd-escape

You can delete an existing binding by leaving the expression empty:

    map gh             # deletes 'gh' mapping
    cmap <c-g>         # deletes '<c-g>' mapping

"cmd" is used to define a custom command

    cmd usage $du -h . | less

You can delete an existing command by leaving the expression empty:

    cmd trash          # deletes trash command

If there is no prefix then ":" is assumed:

    map zt set info time

An explicit ":" can be provided to group statements until a "\n" occurs
which is especially useful for "map" and "cmd" commands:

    map st :set sortby time; set info time

If you need multiline you can wrap statements in "{{" and "}}" after the
proper prefix.

    map st :{{
        set sortby time
        set info time
    }}

Mappings

The usual way to map a key sequence is to assign it to a named or unnamed
command. While this provides a clean way to remap builtin keys as well as
other commands, it can be limiting at times. For this reason "push" command
is provided by lf. This command is used to simulate key pushes given as its
arguments. You can "map" a key to a "push" command with an argument to
create various keybindings.

This is mainly useful for two purposes. First, it can be used to map a
command with a command count:

    map <c-j> push 10j

Second, it can be used to avoid typing the name when a command takes
arguments:

    map r push :rename<space>

One thing to be careful is that since "push" command works with keys instead
of commands it is possible to accidentally create recursive bindings:

    map j push 2j

These types of bindings create a deadlock when executed.

Commands

For demonstration let us write a shell command to move selected file(s) to
trash.

A first attempt to write such a command may look like this:

    cmd trash ${{
        mkdir -p ~/.trash
        if [ -z "$fs" ]; then
            mv --backup=numbered "$f" ~/.trash
        else
            IFS="'printf '\n\t''"; mv --backup=numbered $fs ~/.trash
        fi
    }}

We check "$fs" to see if there are any marked files. Otherwise we just
delete the current file. Since this is such a common pattern, a separate
"$fx" variable is provided. We can use this variable to get rid of the
conditional:

    cmd trash ${{
        mkdir -p ~/.trash
        IFS="'printf '\n\t''"; mv --backup=numbered $fx ~/.trash
    }}

The trash directory is checked each time the command is executed. We can
move it outside of the command so it would only run once at startup:

    ${{ mkdir -p ~/.trash }}

    cmd trash ${{ IFS="'printf '\n\t''"; mv --backup=numbered $fx ~/.trash }}

Since these are one liners, we can drop "{{" and "}}":

    $mkdir -p ~/.trash

    cmd trash $IFS="'printf '\n\t''"; mv --backup=numbered $fx ~/.trash

Finally note that we set "IFS" variable manually in these commands. Instead
we could use the "ifs" option to set it for all shell commands (i.e. 'set
ifs "\n"'). This can be especially useful for interactive use (e.g. "$rm $f"
or "$rm $fs" would simply work). This option is not set by default as it can
behave unexpectedly for new users. However, use of this option is highly
recommended and it is assumed in the rest of the documentation.

Remote Commands

One of the more advanced features in lf is remote commands. All clients
connect to a server on startup. It is possible to send commands to all or
any of the connected clients over the common server. This is used internally
to notify file selection changes to other clients.

To use this feature, you need to use a client which supports communicating
with a UNIX-domain socket. OpenBSD implementation of netcat (nc) is one such
example. You can use it to send a command to the socket file:

    echo 'send echo hello world' | nc -U /tmp/lf.${USER}.sock

Since such a client may not be available everywhere, lf comes bundled with a
command line flag to be used as such. When using lf, you do not need to
specify the address of the socket file. This is the recommended way of using
remote commands since it is shorter and immune to socket file address
changes:

    lf -remote 'send echo hello world'

In this command "send" is used to send the rest of the string as a command
to all connected clients. You can optionally give it an id number to send a
command to a single client:

    lf -remote 'send 1000 echo hello world'

All clients have a unique id number but you may not be aware of the id
number when you are writing a command. For this purpose, an "$id" variable
is exported to the environment for shell commands. You can use it to send a
remote command from a client to the server which in return sends a command
back to itself. So now you can display a message in the current client by
calling the following in a shell command:

    lf -remote "send $id echo hello world"

Since lf does not have control flow syntax, remote commands are used for
such needs. A common use is to display an error message back in the client.
You can implement a safe rename command which does not overwrite an existing
file or directory as such:

    cmd rename ${{
        if [ -e "$1" ]; then
            lf -remote "send $id echo file exists"
        else
            mv "$f" "$1"
        fi
    }}

Besides "send" command, there are also two commands to get or set the
current file selection. Two possible modes "copy" and "move" specify whether
selected files are to be copied or moved. File names are separated ":"
character. Setting the file selection is done with "save" command:

    lf -remote 'save copy foo.txt:bar.txt:baz.txt'

Getting the file selection is similarly done with "load" command. You may
need to parse the response as such to achieve what you need:

    resp=$(echo 'load' | nc -U /tmp/lf.${USER}.sock)
    mode=$(echo $resp | cut -d' ' -f1)
    list=$(echo $resp | cut -d' ' -f2-)
    if [ $mode = 'copy' ]; then
        # do something with the $list
    elif [ $mode = 'move' ]; then
        # do something else with the $list
    fi

Lastly, there is a "conn" command to connect the server as a client. This
should not be needed for users.

File Operations

lf uses the underlying "cp" and "mv" shell commands for file operations. For
this purpose, when you "yank" (i.e. copy) a file, it doesn't actually copy
the file on the disk, but only records its name to memory. The actual file
operation takes place when you do the "put" in which case the "cp" command
is used. Similarly the "mv" command is used for "delete" (i.e. cut or kill)
followed by "put". These traditional names (e.g. "yank", "delete", and
"put") are picked instead of the other common convention (e.g. copy and cut)
to resemble the default keybinds for these operations.

By default, lf does not provide an actual file deletion command to protect
new users. You can define such a command and optionally assign a key if you
like. An example command to move selected files to a trash folder and remove
files completely are provided in the example configuration file.

Opening Files

You can use "open-file" command to open a file. This is a special command
called by "open" when the current file is not a directory. Normally a user
maps the "open" command to a key (default "l") and customize "open-file"
command as desired. You can define it just as you would define any other
command:

    cmd open-file $vim $fx

It is possible to use different command types:

    cmd open-file &xdg-open $f

You may want to use either file extensions or mime types from "file"
command:

    cmd open-file ${{
        case $(file --mime-type $f -b) in
            text/*) vim $fx;;
            *) for f in $fx; do xdg-open $f > /dev/null 2> /dev/null & done;;
        esac
    }}

lf does not come bundled with a file opener. You can use any of the existing
file openers as you like. Possible options are "open" (for Mac OS X only),
"xdg-utils" (executable name is "xdg-open"), "libfile-mimeinfo-perl"
(executable name is "mimeopen"), "rifle" (ranger's default file opener), or
"mimeo" to name a few.

Previewing Files

lf previews files on the preview pane by printing the file until the end or
the preview pane is filled. This output can be enhanced by providing a
custom preview script for filtering. This can be used to highlight source
codes, list contents of archive files or view pdf or image files as text to
name few. For coloring lf recognizes ansi escape codes.

In order to use this feature you need to set the value of "previewer" option
to the path of an executable file. lf passes the current file name as the
first argument and the height of the preview pane as the second argument
when running this file. Output of the execution is printed in the preview
pane. You may want to use the same script in your pager mapping as well if
any:

    set previewer ~/.config/lf/pv.sh
    map i $~/.config/lf/pv.sh $f | less -R

Since this script is called for each file selection change it needs to be as
efficient as possible and this responsibility is left to the user. You may
use file extensions to determine the type of file more efficiently compared
to obtaining mime types from "file" command. Extensions can then be used to
match cleanly within a conditional:

    #!/bin/sh

    case "$1" in
        *.tar*) tar tf "$1";;
        *.zip) unzip -l "$1";;
        *.rar) unrar l "$1";;
        *.7z) 7z l "$1";;
        *.pdf) pdftotext "$1" -;;
        *) highlight -O ansi "$1" || cat "$1";;
    esac

Another important consideration for efficiency is the use of programs with
short startup times for preview. For this reason, "highlight" is recommended
over "pygmentize" for syntax highlighting. Besides, it is also important
that the application is processing the file on the fly rather than first
reading it to the memory and then do the processing afterwards. This is
especially relevant for big files. lf automatically closes the previewer
script output pipe with a SIGPIPE when enough lines are read. When
everything else fails, you can make use of the height argument to only feed
the first portion of the file to a program for preview.
`
<commit_msg>cleanup<commit_after>// DO NOT EDIT! (AUTO-GENERATED)

package main

var genDocString = `
lf is a terminal file manager.

Source code can be found in the repository at
https://github.com/gokcehan/lf.

This documentation can either be read from terminal using 'lf -doc' or
online at https://godoc.org/github.com/gokcehan/lf.

Reference

The following commands are provided by lf with default keybindings:

    up                (default 'k' and '<up>')
    half-up           (default '<c-u>')
    page-up           (default '<c-b>')
    down              (default 'j' and '<down>')
    half-down         (default '<c-d>')
    page-down         (default '<c-f>')
    updir             (default 'h' and '<left>')
    open              (default 'l' and '<right>')
    quit              (default 'q')
    bot               (default 'G')
    top               (default 'gg')
    read              (default ':')
    read-shell        (default '$')
    read-shell-wait   (default '!')
    read-shell-async  (default '&')
    search            (default '/')
    search-back       (default '?')
    search-next       (default 'n')
    search-prev       (default 'N')
    toggle            (default '<space>')
    invert            (default 'v')
    yank              (default 'y')
    clear             (default 'c')
    delete            (default 'd')
    put               (default 'p')
    renew             (default '<c-l>')

The following commands are provided by lf without default keybindings:

    sync  synchronizes yanked/deleted files with server
    echo  prints its arguments to the message line
    cd    changes working directory to its argument
    push  simulate key pushes given in its argument

The following command line commands are provided by lf with default
keybindings:

    cmd-escape        (default '<esc>')
    cmd-comp          (default '<tab>')
    cmd-enter         (default '<c-j>' and '<enter>')
    cmd-hist-next     (default '<c-n>')
    cmd-hist-prev     (default '<c-p>')
    cmd-delete        (default '<c-d>' and '<delete>')
    cmd-delete-back   (default '<bs>' and '<bs2>')
    cmd-left          (default '<c-b>' and '<left>')
    cmd-right         (default '<c-f>' and '<right>')
    cmd-beg           (default '<c-a>' and '<home>')
    cmd-end           (default '<c-e>' and '<end>')
    cmd-delete-end    (default '<c-k>')
    cmd-delete-beg    (default '<c-u>')
    cmd-delete-word   (default '<c-w>')
    cmd-put           (default '<c-y>')
    cmd-transpose     (default '<c-t>')

The following options can be used to customize the behavior of lf:

    dircounts   boolean  (default off)
    dirfirst    boolean  (default on)
    globsearch  boolean  (default off)
    hidden      boolean  (default off)
    ignorecase  boolean  (default off)
    preview     boolean  (default on)
    reverse     boolean  (default off)
    smartcase   boolean  (default off)
    wrapscan    boolean  (default on)
    scrolloff   integer  (default 0)
    tabstop     integer  (default 8)
    filesep     string   (default "\n")
    ifs         string   (default '') (not exported if empty)
    previewer   string   (default '') (not filtered if empty)
    shell       string   (default '/bin/sh')
    sortby      string   (default 'natural')
    timefmt     string   (default 'Mon Jan _2 15:04:05 2006')
    ratios      string   (default '1:2:3')
    info        string   (default '')

The following variables are exported for shell commands:

    $f   current file
    $fs  marked file(s) separated with 'filesep'
    $fx  current file or marked file(s) if any
    $id  id number of the client

Configuration

The configuration file should be located at:

    $XDG_CONFIG_HOME/lf/lfrc

If '$XDG_CONFIG_HOME' is not set, it defaults to '$HOME/.config' so the
location should be:

    ~/.config/lf/lfrc

A sample configuration file can be found at
https://github.com/gokcehan/lf/blob/master/etc/lfrc.example.

Prefixes

The following command prefixes are used by lf:

    :  read (default)    builtin/custom command
    $  read-shell        shell command
    !  read-shell-wait   shell command waiting for key press
    &  read-shell-async  asynchronous shell command
    /  search            search file in current directory
    ?  search-back       search file in the reverse order

The same evaluator is used for the command line and the configuration file.
The difference is that prefixes are not necessary in the command line.
Instead different modes are provided to read corresponding commands. Note
that by default these modes are mapped to the prefix keys above.

Syntax

Characters from '#' to newline are comments and ignored:

    # comments start with '#'

There are three special commands ('set', 'map', and 'cmd') and their
variants for configuration.

'set' is used to set an option which can be boolean, integer, or string:

    set hidden         # boolean on
    set nohidden       # boolean off
    set hidden!        # boolean toggle
    set scrolloff 10   # integer value
    set sortby time    # string value w/o quotes
    set sortby 'time'  # string value with single quotes (whitespaces)
    set sortby "time"  # string value with double quotes (backslash escapes)

'map' is used to bind a key to a command which can be builtin command,
custom command, or shell command:

    map gh cd ~        # builtin command
    map D trash        # custom command
    map i $less $f     # shell command
    map u !du -h .     # waiting shell command

'cmap' is used to bind a key to a command line command which can only be one
of the builtin commands:

    cmap <c-g> cmd-escape

You can delete an existing binding by leaving the expression empty:

    map gh             # deletes 'gh' mapping
    cmap <c-g>         # deletes '<c-g>' mapping

'cmd' is used to define a custom command

    cmd usage $du -h . | less

You can delete an existing command by leaving the expression empty:

    cmd trash          # deletes 'trash' command

If there is no prefix then ':' is assumed:

    map zt set info time

An explicit ':' can be provided to group statements until a newline which is
especially useful for 'map' and 'cmd' commands:

    map st :set sortby time; set info time

If you need multiline you can wrap statements in '{{' and '}}' after the
proper prefix.

    map st :{{
        set sortby time
        set info time
    }}

Mappings

The usual way to map a key sequence is to assign it to a named or unnamed
command. While this provides a clean way to remap builtin keys as well as
other commands, it can be limiting at times. For this reason 'push' command
is provided by lf. This command is used to simulate key pushes given as its
arguments. You can 'map' a key to a 'push' command with an argument to
create various keybindings.

This is mainly useful for two purposes. First, it can be used to map a
command with a command count:

    map <c-j> push 10j

Second, it can be used to avoid typing the name when a command takes
arguments:

    map r push :rename<space>

One thing to be careful is that since 'push' command works with keys instead
of commands it is possible to accidentally create recursive bindings:

    map j push 2j

These types of bindings create a deadlock when executed.

Commands

For demonstration let us write a shell command to move selected file(s) to
trash.

A first attempt to write such a command may look like this:

    cmd trash ${{
        mkdir -p ~/.trash
        if [ -z "$fs" ]; then
            mv --backup=numbered "$f" ~/.trash
        else
            IFS="'printf '\n\t''"; mv --backup=numbered $fs ~/.trash
        fi
    }}

We check '$fs' to see if there are any marked files. Otherwise we just
delete the current file. Since this is such a common pattern, a separate
'$fx' variable is provided. We can use this variable to get rid of the
conditional:

    cmd trash ${{
        mkdir -p ~/.trash
        IFS="'printf '\n\t''"; mv --backup=numbered $fx ~/.trash
    }}

The trash directory is checked each time the command is executed. We can
move it outside of the command so it would only run once at startup:

    ${{ mkdir -p ~/.trash }}

    cmd trash ${{ IFS="'printf '\n\t''"; mv --backup=numbered $fx ~/.trash }}

Since these are one liners, we can drop '{{' and '}}':

    $mkdir -p ~/.trash

    cmd trash $IFS="'printf '\n\t''"; mv --backup=numbered $fx ~/.trash

Finally note that we set 'IFS' variable manually in these commands. Instead
we could use the 'ifs' option to set it for all shell commands (i.e. 'set
ifs "\n"'). This can be especially useful for interactive use (e.g. '$rm $f'
or '$rm $fs' would simply work). This option is not set by default as it can
behave unexpectedly for new users. However, use of this option is highly
recommended and it is assumed in the rest of the documentation.

Remote Commands

One of the more advanced features in lf is remote commands. All clients
connect to a server on startup. It is possible to send commands to all or
any of the connected clients over the common server. This is used internally
to notify file selection changes to other clients.

To use this feature, you need to use a client which supports communicating
with a UNIX-domain socket. OpenBSD implementation of netcat (nc) is one such
example. You can use it to send a command to the socket file:

    echo 'send echo hello world' | nc -U /tmp/lf.${USER}.sock

Since such a client may not be available everywhere, lf comes bundled with a
command line flag to be used as such. When using lf, you do not need to
specify the address of the socket file. This is the recommended way of using
remote commands since it is shorter and immune to socket file address
changes:

    lf -remote 'send echo hello world'

In this command 'send' is used to send the rest of the string as a command
to all connected clients. You can optionally give it an id number to send a
command to a single client:

    lf -remote 'send 1000 echo hello world'

All clients have a unique id number but you may not be aware of the id
number when you are writing a command. For this purpose, an '$id' variable
is exported to the environment for shell commands. You can use it to send a
remote command from a client to the server which in return sends a command
back to itself. So now you can display a message in the current client by
calling the following in a shell command:

    lf -remote "send $id echo hello world"

Since lf does not have control flow syntax, remote commands are used for
such needs. A common use is to display an error message back in the client.
You can implement a safe rename command which does not overwrite an existing
file or directory as such:

    cmd rename ${{
        if [ -e $1 ]; then
            lf -remote "send $id echo file exists"
        else
            mv $f $1
        fi
    }}

Besides 'send' command, there are also two commands to get or set the
current file selection. Two possible modes 'copy' and 'move' specify whether
selected files are to be copied or moved. File names are separated by
newline character. Setting the file selection is done with 'save' command:

    nc -U /tmp/lf.${USER}.sock << 'EOF'
    save
    copy
    foo.txt
    bar.txt
    baz.txt

    EOF

Getting the file selection is similarly done with 'load' command:

    resp=$(echo 'load' | nc -U /tmp/lf.${USER}.sock)
    mode=$(echo "$resp" | sed -n '1p')
    list=$(echo "$resp" | sed '1d')
    if [ $mode = 'copy' ]; then
        # do something with $list
    elif [ $mode = 'move' ]; then
        # do something else with $list
    fi

Lastly, there is a 'conn' command to connect the server as a client. This
should not be needed for users.

File Operations

lf uses the underlying 'cp' and 'mv' shell commands for file operations. For
this purpose, when you 'yank' (i.e. copy) a file, it doesn't actually copy
the file on the disk, but only records its name to memory. The actual file
operation takes place when you do the 'put' in which case the 'cp' command
is used. Similarly the 'mv' command is used for 'delete' (i.e. cut or kill)
followed by 'put'. These traditional names (e.g. 'yank', 'delete', and
'put') are picked instead of the other common convention (e.g. copy and cut)
to resemble the default keybinds for these operations.

By default, lf does not provide an actual file deletion command to protect
new users. You can define such a command and optionally assign a key if you
like. An example command to move selected files to a trash folder and remove
files completely are provided in the example configuration file.

Opening Files

You can use 'open-file' command to open a file. This is a special command
called by 'open' when the current file is not a directory. Normally a user
maps the 'open' command to a key (default 'l') and customize 'open-file'
command as desired. You can define it just as you would define any other
command:

    cmd open-file $vi $fx

It is possible to use different command types:

    cmd open-file &xdg-open $f

You may want to use either file extensions or mime types from 'file'
command:

    cmd open-file ${{
        case $(file --mime-type $f -b) in
            text/*) vi $fx;;
            *) for f in $fx; do xdg-open $f > /dev/null 2> /dev/null & done;;
        esac
    }}

lf does not come bundled with a file opener. You can use any of the existing
file openers as you like. Possible options are 'open' (for Mac OS X only),
'xdg-utils' (executable name is 'xdg-open'), 'libfile-mimeinfo-perl'
(executable name is 'mimeopen'), 'rifle' (ranger's default file opener), or
'mimeo' to name a few.

Previewing Files

lf previews files on the preview pane by printing the file until the end or
the preview pane is filled. This output can be enhanced by providing a
custom preview script for filtering. This can be used to highlight source
codes, list contents of archive files or view pdf or image files as text to
name few. For coloring lf recognizes ansi escape codes.

In order to use this feature you need to set the value of 'previewer' option
to the path of an executable file. lf passes the current file name as the
first argument and the height of the preview pane as the second argument
when running this file. Output of the execution is printed in the preview
pane. You may want to use the same script in your pager mapping as well if
any:

    set previewer ~/.config/lf/pv.sh
    map i $~/.config/lf/pv.sh $f | less -R

Since this script is called for each file selection change it needs to be as
efficient as possible and this responsibility is left to the user. You may
use file extensions to determine the type of file more efficiently compared
to obtaining mime types from 'file' command. Extensions can then be used to
match cleanly within a conditional:

    #!/bin/sh

    case "$1" in
        *.tar*) tar tf "$1";;
        *.zip) unzip -l "$1";;
        *.rar) unrar l "$1";;
        *.7z) 7z l "$1";;
        *.pdf) pdftotext "$1" -;;
        *) highlight -O ansi "$1" || cat "$1";;
    esac

Another important consideration for efficiency is the use of programs with
short startup times for preview. For this reason, 'highlight' is recommended
over 'pygmentize' for syntax highlighting. Besides, it is also important
that the application is processing the file on the fly rather than first
reading it to the memory and then do the processing afterwards. This is
especially relevant for big files. lf automatically closes the previewer
script output pipe with a SIGPIPE when enough lines are read. When
everything else fails, you can make use of the height argument to only feed
the first portion of the file to a program for preview.
`
<|endoftext|>


<commit_before>package userValidator

import (
	"net/http"
	"testing"

	msg "github.com/NyaaPantsu/nyaa/utils/messages"
	"github.com/NyaaPantsu/nyaa/utils/validator"
	"github.com/gin-gonic/gin"
)

func TestForms(t *testing.T) {
	t.Parallel()
	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatal(err)
	}
	c := &gin.Context{Request: req}
	messages := msg.GetMessages(c)
	registration := &RegistrationForm{
		"lol", "", "testing", "testing", "xxx", "true",
	}
	login := &LoginForm{"lol", "testing", "/"}
	user := &UserForm{"lol", "", "", "testing", "testing", "testing", 0, ""}
	userSettings := &UserSettingsForm{}
	password := &PasswordForm{"testing", "testing"}
	passwordReset := &SendPasswordResetForm{"lol@gt.com"}
	passwordResetForm := &PasswordResetForm{"testing", "testing"}

	validator.ValidateForm(registration, messages)
	if messages.HasErrors() {
		t.Errorf("Error on RegistrationForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(login, messages)
	if messages.HasErrors() {
		t.Errorf("Error on LoginForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(user, messages)
	if messages.HasErrors() {
		t.Errorf("Error on User struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(userSettings, messages)
	if messages.HasErrors() {
		t.Errorf("Error on UserSettingsForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(password, messages)
	if messages.HasErrors() {
		t.Errorf("Error on PasswordForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(passwordReset, messages)
	if messages.HasErrors() {
		t.Errorf("Error on SendPasswordResetForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(passwordResetForm, messages)
	if messages.HasErrors() {
		t.Errorf("Error on PasswordResetForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
}
<commit_msg>Fixing form registration tests<commit_after>package userValidator

import (
	"net/http"
	"testing"

	msg "github.com/NyaaPantsu/nyaa/utils/messages"
	"github.com/NyaaPantsu/nyaa/utils/validator"
	"github.com/gin-gonic/gin"
)

func TestForms(t *testing.T) {
	t.Parallel()
	req, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatal(err)
	}
	c := &gin.Context{Request: req}
	messages := msg.GetMessages(c)
	registration := &RegistrationForm{
		"lol", "", "testing", "testing", "xxx", "1",
	}
	login := &LoginForm{"lol", "testing", "/"}
	user := &UserForm{"lol", "", "", "testing", "testing", "testing", 0, ""}
	userSettings := &UserSettingsForm{}
	password := &PasswordForm{"testing", "testing"}
	passwordReset := &SendPasswordResetForm{"lol@gt.com"}
	passwordResetForm := &PasswordResetForm{"testing", "testing"}

	validator.ValidateForm(registration, messages)
	if messages.HasErrors() {
		t.Errorf("Error on RegistrationForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(login, messages)
	if messages.HasErrors() {
		t.Errorf("Error on LoginForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(user, messages)
	if messages.HasErrors() {
		t.Errorf("Error on User struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(userSettings, messages)
	if messages.HasErrors() {
		t.Errorf("Error on UserSettingsForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(password, messages)
	if messages.HasErrors() {
		t.Errorf("Error on PasswordForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(passwordReset, messages)
	if messages.HasErrors() {
		t.Errorf("Error on SendPasswordResetForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
	validator.ValidateForm(passwordResetForm, messages)
	if messages.HasErrors() {
		t.Errorf("Error on PasswordResetForm struct, please check validation arguments: %v", messages.GetAllErrors())
	}
}
<|endoftext|>


<commit_before>package lib

const (
	// Version is the version number the client believes it is
	Version = "3.0"
)
<commit_msg>bump version.go<commit_after>package lib

const (
	// Version is the version number the client believes it is
	Version = "3.1"
)
<|endoftext|>


<commit_before>package algoliaconnector

import "errors"

var ErrDataNotValid = errors.New("Algolia error: invalid data")

func (f *Controller) insert(indexName string, record map[string]interface{}) error {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return err
	}
	_, err = index.AddObject(record)
	return err
}

// Delete removes a record from the given indexName
func (f *Controller) delete(indexName string, objectID string) error {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return err
	}
	_, err = index.DeleteObject(objectID)
	return err
}

func (f *Controller) get(indexName string, objectId string) (map[string]interface{}, error) {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return nil, err
	}

	record, err := index.GetObject(objectId)
	if err != nil {
		return nil, err
	}

	castRecord, ok := record.(map[string]interface{})
	if !ok {
		return nil, ErrDataNotValid
	}

	return castRecord, nil
}

func (f *Controller) partialUpdate(indexName string, record map[string]interface{}) error {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return err
	}

	if _, err = index.PartialUpdateObject(record); err != nil {
		return err
	}

	return nil
}

func removeTag(record map[string]interface{}, channelId string) []interface{} {
	if record == nil {
		return []interface{}{}
	}

	tagsDoc, ok := record["_tags"]
	if !ok {
		return []interface{}{}
	}

	tags, tagsOk := tagsDoc.([]interface{})
	if !tagsOk {
		return []interface{}{}
	}

	newTags := make([]interface{}, 0)
	for _, ele := range tags {
		if ele != channelId {
			newTags = append(newTags, ele)
		}
	}

	return newTags
}

func appendTag(record map[string]interface{}, channelId string) []interface{} {
	if record == nil {
		return []interface{}{channelId}
	}

	tagsDoc, ok := record["_tags"]
	if !ok {
		return []interface{}{channelId}
	}

	tags, tagsOk := tagsDoc.([]interface{})
	if !tagsOk {
		return []interface{}{channelId}
	}
	for _, ele := range tags {
		if ele == channelId {
			return tags
		}
	}

	return append(tags, channelId)
}

func removeMessageTag(record map[string]interface{}, channelId string) []interface{} {
	if record == nil {
		return nil
	}

	filtered := make([]interface{}, 0)

	tagsDoc, ok := record["_tags"]
	if !ok {
		return nil
	}

	tags, tagsOk := tagsDoc.([]interface{})
	if !tagsOk {
		return nil
	}

	for _, ele := range tags {
		id := ele.(string)
		if id == channelId {
			continue
		}
		filtered = append(filtered, id)
	}

	return filtered
}
<commit_msg>Socialapi: remove unused code<commit_after>package algoliaconnector

import "errors"

var ErrDataNotValid = errors.New("Algolia error: invalid data")

func (f *Controller) insert(indexName string, record map[string]interface{}) error {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return err
	}
	_, err = index.AddObject(record)
	return err
}

// Delete removes a record from the given indexName
func (f *Controller) delete(indexName string, objectID string) error {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return err
	}
	_, err = index.DeleteObject(objectID)
	return err
}

func (f *Controller) get(indexName string, objectId string) (map[string]interface{}, error) {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return nil, err
	}

	record, err := index.GetObject(objectId)
	if err != nil {
		return nil, err
	}

	castRecord, ok := record.(map[string]interface{})
	if !ok {
		return nil, ErrDataNotValid
	}

	return castRecord, nil
}

func (f *Controller) partialUpdate(indexName string, record map[string]interface{}) error {
	index, err := f.indexes.GetIndex(indexName)
	if err != nil {
		return err
	}

	if _, err = index.PartialUpdateObject(record); err != nil {
		return err
	}

	return nil
}

func appendTag(record map[string]interface{}, channelId string) []interface{} {
	if record == nil {
		return []interface{}{channelId}
	}

	tagsDoc, ok := record["_tags"]
	if !ok {
		return []interface{}{channelId}
	}

	tags, tagsOk := tagsDoc.([]interface{})
	if !tagsOk {
		return []interface{}{channelId}
	}
	for _, ele := range tags {
		if ele == channelId {
			return tags
		}
	}

	return append(tags, channelId)
}

func removeMessageTag(record map[string]interface{}, channelId string) []interface{} {
	if record == nil {
		return nil
	}

	filtered := make([]interface{}, 0)

	tagsDoc, ok := record["_tags"]
	if !ok {
		return nil
	}

	tags, tagsOk := tagsDoc.([]interface{})
	if !tagsOk {
		return nil
	}

	for _, ele := range tags {
		id := ele.(string)
		if id == channelId {
			continue
		}
		filtered = append(filtered, id)
	}

	return filtered
}
<|endoftext|>


<commit_before>package api

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/ansible-semaphore/semaphore/api/helpers"
	"github.com/ansible-semaphore/semaphore/db"
	"github.com/go-ldap/ldap/v3"

	log "github.com/Sirupsen/logrus"
	"github.com/ansible-semaphore/semaphore/util"

	"golang.org/x/crypto/bcrypt"
)

func tryFindLDAPUser(username, password string) (*db.User, error) {
	if !util.Config.LdapEnable {
		return nil, fmt.Errorf("LDAP not configured")
	}

	var l *ldap.Conn
	var err error
	if util.Config.LdapNeedTLS {
		l, err = ldap.DialTLS("tcp", util.Config.LdapServer, &tls.Config{
			InsecureSkipVerify: true,
		})
	} else {
		l, err = ldap.Dial("tcp", util.Config.LdapServer)
	}

	if err != nil {
		return nil, err
	}
	defer l.Close()

	// First bind with a read only user
	if err = l.Bind(util.Config.LdapBindDN, util.Config.LdapBindPassword); err != nil {
		return nil, err
	}

	// Search for the given username
	searchRequest := ldap.NewSearchRequest(
		util.Config.LdapSearchDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf(util.Config.LdapSearchFilter, username),
		[]string{util.Config.LdapMappings.DN},
		nil,
	)

	sr, err := l.Search(searchRequest)
	if err != nil {
		return nil, err
	}

	if len(sr.Entries) < 1 {
		return nil, nil
	}

	if len(sr.Entries) > 1 {
		return nil, fmt.Errorf("too many entries returned")
	}

	// Bind as the user to verify their password
	userdn := sr.Entries[0].DN
	if err = l.Bind(userdn, password); err != nil {
		return nil, err
	}

	// Get user info and ensure authentication in case LDAP supports unauthenticated bind
	searchRequest = ldap.NewSearchRequest(
		util.Config.LdapSearchDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf(util.Config.LdapSearchFilter, username),
		[]string{util.Config.LdapMappings.DN, util.Config.LdapMappings.Mail, util.Config.LdapMappings.UID, util.Config.LdapMappings.CN},
		nil,
	)

	sr, err = l.Search(searchRequest)
	if err != nil {
		return nil, err
	}

	ldapUser := db.User{
		Username: strings.ToLower(sr.Entries[0].GetAttributeValue(util.Config.LdapMappings.UID)),
		Created:  time.Now(),
		Name:     sr.Entries[0].GetAttributeValue(util.Config.LdapMappings.CN),
		Email:    sr.Entries[0].GetAttributeValue(util.Config.LdapMappings.Mail),
		External: true,
		Alert:    false,
	}

	log.Info("User " + ldapUser.Name + " with email " + ldapUser.Email + " authorized via LDAP correctly")
	return &ldapUser, nil
}

// createSession creates session for passed user and stores session details
// in cookies.
func createSession(w http.ResponseWriter, r *http.Request, user db.User) {
	newSession, err := helpers.Store(r).CreateSession(db.Session{
		UserID:     user.ID,
		Created:    time.Now(),
		LastActive: time.Now(),
		IP:         r.Header.Get("X-Real-IP"),
		UserAgent:  r.Header.Get("user-agent"),
		Expired:    false,
	})

	if err != nil {
		panic(err)
	}

	encoded, err := util.Cookie.Encode("semaphore", map[string]interface{}{
		"user":    user.ID,
		"session": newSession.ID,
	})
	if err != nil {
		panic(err)
	}

	http.SetCookie(w, &http.Cookie{
		Name:  "semaphore",
		Value: encoded,
		Path:  "/",
	})
}

//nolint: gocyclo
func login(w http.ResponseWriter, r *http.Request) {
	var login struct {
		Auth     string `json:"auth" binding:"required"`
		Password string `json:"password" binding:"required"`
	}
	if !helpers.Bind(w, r, &login) {
		return
	}

	/*
		logic:
		- fetch user from ldap if enabled
		- fetch user from database by username/email
		- create user in database if doesn't exist & ldap record found
		- check password if non-ldap user
		- create session & send cookie
	*/

	login.Auth = strings.ToLower(login.Auth)

	var err error

	var ldapUser *db.User

	if util.Config.LdapEnable {
		ldapUser, err = tryFindLDAPUser(login.Auth, login.Password)
		if err != nil {
			log.Info(err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	user, err := helpers.Store(r).GetUserByLoginOrEmail(login.Auth, login.Auth)

	if err == db.ErrNotFound {
		if ldapUser != nil {
			// create new LDAP user
			user, err = helpers.Store(r).CreateUserWithoutPassword(*ldapUser)
			if err != nil {
				panic(err)
			}
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	} else if err != nil {
		panic(err)
	}

	// check if ldap user & no ldap user found
	if user.External && ldapUser == nil {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// non-ldap login
	if !user.External {
		if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(login.Password)); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		// authenticated.
	}

	createSession(w, r, user)

	w.WriteHeader(http.StatusNoContent)
}

func logout(w http.ResponseWriter, r *http.Request) {
	http.SetCookie(w, &http.Cookie{
		Name:    "semaphore",
		Value:   "",
		Expires: time.Now().Add(24 * 7 * time.Hour * -1),
		Path:    "/",
	})

	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>refactor(be): simplify ldap auth code<commit_after>package api

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/ansible-semaphore/semaphore/api/helpers"
	"github.com/ansible-semaphore/semaphore/db"
	"github.com/go-ldap/ldap/v3"

	log "github.com/Sirupsen/logrus"
	"github.com/ansible-semaphore/semaphore/util"

	"golang.org/x/crypto/bcrypt"
)

func tryFindLDAPUser(username, password string) (*db.User, error) {
	if !util.Config.LdapEnable {
		return nil, fmt.Errorf("LDAP not configured")
	}

	var l *ldap.Conn
	var err error
	if util.Config.LdapNeedTLS {
		l, err = ldap.DialTLS("tcp", util.Config.LdapServer, &tls.Config{
			InsecureSkipVerify: true,
		})
	} else {
		l, err = ldap.Dial("tcp", util.Config.LdapServer)
	}

	if err != nil {
		return nil, err
	}
	defer l.Close()

	// First bind with a read only user
	if err = l.Bind(util.Config.LdapBindDN, util.Config.LdapBindPassword); err != nil {
		return nil, err
	}

	// Search for the given username
	searchRequest := ldap.NewSearchRequest(
		util.Config.LdapSearchDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf(util.Config.LdapSearchFilter, username),
		[]string{util.Config.LdapMappings.DN},
		nil,
	)

	sr, err := l.Search(searchRequest)
	if err != nil {
		return nil, err
	}

	if len(sr.Entries) < 1 {
		return nil, nil
	}

	if len(sr.Entries) > 1 {
		return nil, fmt.Errorf("too many entries returned")
	}

	// Bind as the user to verify their password
	userdn := sr.Entries[0].DN
	if err = l.Bind(userdn, password); err != nil {
		return nil, err
	}

	// Get user info and ensure authentication in case LDAP supports unauthenticated bind
	searchRequest = ldap.NewSearchRequest(
		util.Config.LdapSearchDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf(util.Config.LdapSearchFilter, username),
		[]string{util.Config.LdapMappings.DN, util.Config.LdapMappings.Mail, util.Config.LdapMappings.UID, util.Config.LdapMappings.CN},
		nil,
	)

	sr, err = l.Search(searchRequest)
	if err != nil {
		return nil, err
	}

	ldapUser := db.User{
		Username: strings.ToLower(sr.Entries[0].GetAttributeValue(util.Config.LdapMappings.UID)),
		Created:  time.Now(),
		Name:     sr.Entries[0].GetAttributeValue(util.Config.LdapMappings.CN),
		Email:    sr.Entries[0].GetAttributeValue(util.Config.LdapMappings.Mail),
		External: true,
		Alert:    false,
	}

	log.Info("User " + ldapUser.Name + " with email " + ldapUser.Email + " authorized via LDAP correctly")
	return &ldapUser, nil
}

// createSession creates session for passed user and stores session details
// in cookies.
func createSession(w http.ResponseWriter, r *http.Request, user db.User) {
	newSession, err := helpers.Store(r).CreateSession(db.Session{
		UserID:     user.ID,
		Created:    time.Now(),
		LastActive: time.Now(),
		IP:         r.Header.Get("X-Real-IP"),
		UserAgent:  r.Header.Get("user-agent"),
		Expired:    false,
	})

	if err != nil {
		panic(err)
	}

	encoded, err := util.Cookie.Encode("semaphore", map[string]interface{}{
		"user":    user.ID,
		"session": newSession.ID,
	})
	if err != nil {
		panic(err)
	}

	http.SetCookie(w, &http.Cookie{
		Name:  "semaphore",
		Value: encoded,
		Path:  "/",
	})
}

//nolint: gocyclo
func login(w http.ResponseWriter, r *http.Request) {
	var login struct {
		Auth     string `json:"auth" binding:"required"`
		Password string `json:"password" binding:"required"`
	}
	if !helpers.Bind(w, r, &login) {
		return
	}

	/*
		logic:
		- fetch user from ldap if enabled
		- fetch user from database by username/email
		- create user in database if doesn't exist & ldap record found
		- check password if non-ldap user
		- create session & send cookie
	*/

	login.Auth = strings.ToLower(login.Auth)

	var err error

	var ldapUser *db.User

	if util.Config.LdapEnable {
		ldapUser, err = tryFindLDAPUser(login.Auth, login.Password)
		if err != nil {
			log.Info(err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	user, err := helpers.Store(r).GetUserByLoginOrEmail(login.Auth, login.Auth)

	if err == db.ErrNotFound {
		if ldapUser == nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		user, err = helpers.Store(r).CreateUserWithoutPassword(*ldapUser)
	}

	if err != nil {
		panic(err)
	}

	// check if ldap user & no ldap user found
	if user.External && ldapUser == nil {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// non-ldap login
	if !user.External {
		if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(login.Password)); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		// authenticated.
	}

	createSession(w, r, user)

	w.WriteHeader(http.StatusNoContent)
}

func logout(w http.ResponseWriter, r *http.Request) {
	http.SetCookie(w, &http.Cookie{
		Name:    "semaphore",
		Value:   "",
		Expires: time.Now().Add(24 * 7 * time.Hour * -1),
		Path:    "/",
	})

	w.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"go/format"
	"os"
	"text/template"
)

func (self *FileData) generateCode() error {
	if len(self.Enums) == 0 {
		return nil
	}

	self.GatherEnumImports()
	self.GatherStructImports()

	// Execute the template on the data gathered
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, self); err != nil {
		return err
	}

	// Run the go code formatter to make sure syntax is correct before writing.
	b, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	file, err := os.Create(self.File)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write(b)
	return err
}

var tmpl = template.Must(template.New("generate_golific").Parse(
	struct_tmpl +
		enum_tmpl +
		`/****************************************************************************
	This file was generated by Golific.

	Do not edit this file. If you do, your changes will be overwritten the next
	time 'generate' is invoked.
******************************************************************************/

package {{.Package}}

import (
  {{- range $imp, $_ := .Imports}}
  {{printf "%q" $imp -}}
  {{end -}}
)

{{- if .DoStructSummary}}
/******************************************************************************
  STRUCT SUMMARY

******************************************************************************/
{{end -}}

{{- if .DoEnumSummary}}
/******************************************************************************
	ENUM SUMMARY
{{range $enum := .Enums}}
{{- if $enum.DoSummary}}
{{$enum.Name}} (type {{printf "%sEnum" $enum.Name}}, {{$enum.GetIntType}})
{{- range $f := $enum.Fields}}
	{{ printf "%s %d %q %q" $f.Name $f.Value $f.String $f.Description -}}
{{end}}
{{end -}}
{{end -}}
******************************************************************************/
{{end -}}

{{- template "generate_struct" .Structs}}

{{- template "generate_enum" .Enums}}

`))
<commit_msg>bugfix, now checkf len of both Enums and Structs<commit_after>package main

import (
	"bytes"
	"go/format"
	"os"
	"text/template"
)

func (self *FileData) generateCode() error {
	if len(self.Enums) == 0 && len(self.Structs) == 0 {
		return nil
	}

	self.GatherEnumImports()
	self.GatherStructImports()

	// Execute the template on the data gathered
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, self); err != nil {
		return err
	}

	// Run the go code formatter to make sure syntax is correct before writing.
	b, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	file, err := os.Create(self.File)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write(b)
	return err
}

var tmpl = template.Must(template.New("generate_golific").Parse(
	struct_tmpl +
		enum_tmpl +
		`/****************************************************************************
	This file was generated by Golific.

	Do not edit this file. If you do, your changes will be overwritten the next
	time 'generate' is invoked.
******************************************************************************/

package {{.Package}}

import (
  {{- range $imp, $_ := .Imports}}
  {{printf "%q" $imp -}}
  {{end -}}
)

{{- if .DoStructSummary}}
/******************************************************************************
  STRUCT SUMMARY

******************************************************************************/
{{end -}}

{{- if .DoEnumSummary}}
/******************************************************************************
	ENUM SUMMARY
{{range $enum := .Enums}}
{{- if $enum.DoSummary}}
{{$enum.Name}} (type {{printf "%sEnum" $enum.Name}}, {{$enum.GetIntType}})
{{- range $f := $enum.Fields}}
	{{ printf "%s %d %q %q" $f.Name $f.Value $f.String $f.Description -}}
{{end}}
{{end -}}
{{end -}}
******************************************************************************/
{{end -}}

{{- template "generate_struct" .Structs}}

{{- template "generate_enum" .Enums}}

`))
<|endoftext|>


<commit_before>package etcd

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	"github.com/coreos/etcd/config"
)

func TestMultipleNodes(t *testing.T) {
	tests := []int{1, 3, 5, 9, 11}

	for _, tt := range tests {
		es, hs := buildCluster(tt, false)
		waitCluster(t, es)
		for i := range es {
			es[len(es)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func TestMultipleTLSNodes(t *testing.T) {
	tests := []int{1, 3, 5}

	for _, tt := range tests {
		es, hs := buildCluster(tt, true)
		waitCluster(t, es)
		for i := range es {
			es[len(es)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func TestV2Redirect(t *testing.T) {
	es, hs := buildCluster(3, false)
	waitCluster(t, es)
	u := hs[1].URL
	ru := fmt.Sprintf("%s%s", hs[0].URL, "/v2/keys/foo")
	tc := NewTestClient()

	v := url.Values{}
	v.Set("value", "XXX")
	resp, _ := tc.PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo"), v)
	if resp.StatusCode != http.StatusTemporaryRedirect {
		t.Errorf("status = %d, want %d", resp.StatusCode, http.StatusTemporaryRedirect)
	}
	location, err := resp.Location()
	if err != nil {
		t.Errorf("want err = %, want nil", err)
	}

	if location.String() != ru {
		t.Errorf("location = %v, want %v", location.String(), ru)
	}

	resp.Body.Close()
	for i := range es {
		es[len(es)-i-1].Stop()
	}
	for i := range hs {
		hs[len(hs)-i-1].Close()
	}
	afterTest(t)
}

func TestAdd(t *testing.T) {
	tests := []struct {
		size  int
		round int
	}{
		{3, 5},
		{4, 5},
		{5, 5},
		{6, 5},
	}

	for _, tt := range tests {
		es := make([]*Server, tt.size)
		hs := make([]*httptest.Server, tt.size)
		for i := 0; i < tt.size; i++ {
			c := config.New()
			if i > 0 {
				c.Peers = []string{hs[0].URL}
			}
			es[i], hs[i] = initTestServer(c, int64(i), false)
		}

		go es[0].Bootstrap()

		for i := 1; i < tt.size; i++ {
			id := int64(i)
			for {
				lead := es[0].node.Leader()
				if lead == -1 {
					time.Sleep(defaultElection * es[0].tickDuration)
					continue
				}

				err := es[lead].Add(id, es[id].raftPubAddr, es[id].pubAddr)
				if err == nil {
					break
				}
				switch err {
				case tmpErr:
					time.Sleep(defaultElection * es[0].tickDuration)
				case serverStopErr:
					t.Fatalf("#%d on %d: unexpected stop", i, lead)
				default:
					t.Fatal(err)
				}
			}
			go es[i].run()

			for j := 0; j <= i; j++ {
				p := fmt.Sprintf("%s/%d", v2machineKVPrefix, id)
				w, err := es[j].Watch(p, false, false, 1)
				if err != nil {
					t.Errorf("#%d on %d: %v", i, j, err)
					break
				}
				<-w.EventChan
			}
		}

		for i := range hs {
			es[len(hs)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func TestRemove(t *testing.T) {
	tests := []int{3, 4, 5, 6}

	for _, tt := range tests {
		es, hs := buildCluster(tt, false)
		waitCluster(t, es)

		// we don't remove the machine from 2-node cluster because it is
		// not 100 percent safe in our raft.
		// TODO(yichengq): improve it later.
		for i := 0; i < tt-2; i++ {
			id := int64(i)
			send := id
			for {
				send++
				if send > int64(tt-1) {
					send = id
				}

				lead := es[send].node.Leader()
				if lead == -1 {
					time.Sleep(defaultElection * 5 * time.Millisecond)
					continue
				}

				err := es[lead].Remove(id)
				if err == nil {
					break
				}
				switch err {
				case tmpErr:
					time.Sleep(defaultElection * 5 * time.Millisecond)
				case serverStopErr:
					if lead == id {
						break
					}
				default:
					t.Fatal(err)
				}
			}
			<-es[i].stop
		}

		for i := range es {
			es[len(hs)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func buildCluster(number int, tls bool) ([]*Server, []*httptest.Server) {
	bootstrapper := 0
	es := make([]*Server, number)
	hs := make([]*httptest.Server, number)
	var seed string

	for i := range es {
		c := config.New()
		c.Peers = []string{seed}
		es[i], hs[i] = initTestServer(c, int64(i), tls)

		if i == bootstrapper {
			seed = hs[i].URL
			go es[i].Bootstrap()
		} else {
			// wait for the previous configuration change to be committed
			// or this configuration request might be dropped
			w, err := es[0].Watch(v2machineKVPrefix, true, false, uint64(i))
			if err != nil {
				panic(err)
			}
			<-w.EventChan
			go es[i].Join()
		}
	}
	return es, hs
}

func initTestServer(c *config.Config, id int64, tls bool) (e *Server, h *httptest.Server) {
	e = New(c, id)
	e.SetTick(time.Millisecond * 5)
	m := http.NewServeMux()
	m.Handle("/", e)
	m.Handle("/raft", e.t)
	m.Handle("/raft/", e.t)

	if tls {
		h = httptest.NewTLSServer(m)
	} else {
		h = httptest.NewServer(m)
	}

	e.raftPubAddr = h.URL
	e.pubAddr = h.URL
	return
}

func waitCluster(t *testing.T, es []*Server) {
	n := len(es)
	for i, e := range es {
		var index uint64
		for k := 0; k < n; k++ {
			index++
			w, err := e.Watch(v2machineKVPrefix, true, false, index)
			if err != nil {
				panic(err)
			}
			v := <-w.EventChan
			// join command may appear several times due to retry
			// when timeout
			if k > 0 {
				pw := fmt.Sprintf("%s/%d", v2machineKVPrefix, k-1)
				if v.Node.Key == pw {
					continue
				}
			}
			ww := fmt.Sprintf("%s/%d", v2machineKVPrefix, k)
			if v.Node.Key != ww {
				t.Errorf("#%d path = %v, want %v", i, v.Node.Key, ww)
			}
		}
	}
}
<commit_msg>etcd: clean testAdd<commit_after>package etcd

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	"github.com/coreos/etcd/config"
)

func TestMultipleNodes(t *testing.T) {
	tests := []int{1, 3, 5, 9, 11}

	for _, tt := range tests {
		es, hs := buildCluster(tt, false)
		waitCluster(t, es)
		for i := range es {
			es[len(es)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func TestMultipleTLSNodes(t *testing.T) {
	tests := []int{1, 3, 5}

	for _, tt := range tests {
		es, hs := buildCluster(tt, true)
		waitCluster(t, es)
		for i := range es {
			es[len(es)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func TestV2Redirect(t *testing.T) {
	es, hs := buildCluster(3, false)
	waitCluster(t, es)
	u := hs[1].URL
	ru := fmt.Sprintf("%s%s", hs[0].URL, "/v2/keys/foo")
	tc := NewTestClient()

	v := url.Values{}
	v.Set("value", "XXX")
	resp, _ := tc.PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo"), v)
	if resp.StatusCode != http.StatusTemporaryRedirect {
		t.Errorf("status = %d, want %d", resp.StatusCode, http.StatusTemporaryRedirect)
	}
	location, err := resp.Location()
	if err != nil {
		t.Errorf("want err = %, want nil", err)
	}

	if location.String() != ru {
		t.Errorf("location = %v, want %v", location.String(), ru)
	}

	resp.Body.Close()
	for i := range es {
		es[len(es)-i-1].Stop()
	}
	for i := range hs {
		hs[len(hs)-i-1].Close()
	}
	afterTest(t)
}

func TestAdd(t *testing.T) {
	tests := []int{3, 4, 5, 6}

	for _, tt := range tests {
		es := make([]*Server, tt)
		hs := make([]*httptest.Server, tt)
		for i := 0; i < tt; i++ {
			c := config.New()
			if i > 0 {
				c.Peers = []string{hs[0].URL}
			}
			es[i], hs[i] = initTestServer(c, int64(i), false)
		}

		go es[0].Bootstrap()

		for i := 1; i < tt; i++ {
			id := int64(i)
			for {
				lead := es[0].node.Leader()
				if lead == -1 {
					time.Sleep(defaultElection * es[0].tickDuration)
					continue
				}

				err := es[lead].Add(id, es[id].raftPubAddr, es[id].pubAddr)
				if err == nil {
					break
				}
				switch err {
				case tmpErr:
					time.Sleep(defaultElection * es[0].tickDuration)
				case serverStopErr:
					t.Fatalf("#%d on %d: unexpected stop", i, lead)
				default:
					t.Fatal(err)
				}
			}
			go es[i].run()

			for j := 0; j <= i; j++ {
				p := fmt.Sprintf("%s/%d", v2machineKVPrefix, id)
				w, err := es[j].Watch(p, false, false, 1)
				if err != nil {
					t.Errorf("#%d on %d: %v", i, j, err)
					break
				}
				<-w.EventChan
			}
		}

		for i := range hs {
			es[len(hs)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func TestRemove(t *testing.T) {
	tests := []int{3, 4, 5, 6}

	for _, tt := range tests {
		es, hs := buildCluster(tt, false)
		waitCluster(t, es)

		// we don't remove the machine from 2-node cluster because it is
		// not 100 percent safe in our raft.
		// TODO(yichengq): improve it later.
		for i := 0; i < tt-2; i++ {
			id := int64(i)
			send := id
			for {
				send++
				if send > int64(tt-1) {
					send = id
				}

				lead := es[send].node.Leader()
				if lead == -1 {
					time.Sleep(defaultElection * 5 * time.Millisecond)
					continue
				}

				err := es[lead].Remove(id)
				if err == nil {
					break
				}
				switch err {
				case tmpErr:
					time.Sleep(defaultElection * 5 * time.Millisecond)
				case serverStopErr:
					if lead == id {
						break
					}
				default:
					t.Fatal(err)
				}
			}
			<-es[i].stop
		}

		for i := range es {
			es[len(hs)-i-1].Stop()
		}
		for i := range hs {
			hs[len(hs)-i-1].Close()
		}
	}
	afterTest(t)
}

func buildCluster(number int, tls bool) ([]*Server, []*httptest.Server) {
	bootstrapper := 0
	es := make([]*Server, number)
	hs := make([]*httptest.Server, number)
	var seed string

	for i := range es {
		c := config.New()
		c.Peers = []string{seed}
		es[i], hs[i] = initTestServer(c, int64(i), tls)

		if i == bootstrapper {
			seed = hs[i].URL
			go es[i].Bootstrap()
		} else {
			// wait for the previous configuration change to be committed
			// or this configuration request might be dropped
			w, err := es[0].Watch(v2machineKVPrefix, true, false, uint64(i))
			if err != nil {
				panic(err)
			}
			<-w.EventChan
			go es[i].Join()
		}
	}
	return es, hs
}

func initTestServer(c *config.Config, id int64, tls bool) (e *Server, h *httptest.Server) {
	e = New(c, id)
	e.SetTick(time.Millisecond * 5)
	m := http.NewServeMux()
	m.Handle("/", e)
	m.Handle("/raft", e.t)
	m.Handle("/raft/", e.t)

	if tls {
		h = httptest.NewTLSServer(m)
	} else {
		h = httptest.NewServer(m)
	}

	e.raftPubAddr = h.URL
	e.pubAddr = h.URL
	return
}

func waitCluster(t *testing.T, es []*Server) {
	n := len(es)
	for i, e := range es {
		var index uint64
		for k := 0; k < n; k++ {
			index++
			w, err := e.Watch(v2machineKVPrefix, true, false, index)
			if err != nil {
				panic(err)
			}
			v := <-w.EventChan
			// join command may appear several times due to retry
			// when timeout
			if k > 0 {
				pw := fmt.Sprintf("%s/%d", v2machineKVPrefix, k-1)
				if v.Node.Key == pw {
					continue
				}
			}
			ww := fmt.Sprintf("%s/%d", v2machineKVPrefix, k)
			if v.Node.Key != ww {
				t.Errorf("#%d path = %v, want %v", i, v.Node.Key, ww)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os"
	"path"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
	"github.com/ovh/cds/sdk/plugin"
)

func (w *currentWorker) runBuiltin(ctx context.Context, a *sdk.Action, pbJob sdk.PipelineBuildJob, stepOrder int) sdk.Result {
	defer w.drainLogsAndCloseLogger(ctx)

	res := sdk.Result{Status: sdk.StatusFail.String()}
	switch a.Name {
	case sdk.ArtifactUpload:
		filePattern, tag := getArtifactParams(a)
		return w.runArtifactUpload(ctx, filePattern, tag, pbJob, stepOrder)
	case sdk.ArtifactDownload:
		return w.runArtifactDownload(ctx, a, pbJob, stepOrder)
	case sdk.ScriptAction:
		return w.runScriptAction(ctx, a, pbJob, stepOrder)
	case sdk.JUnitAction:
		return w.runParseJunitTestResultAction(ctx, a, pbJob, stepOrder)
	case sdk.GitCloneAction:
		return w.runGitClone(ctx, a, pbJob, stepOrder)
	}
	res.Reason = fmt.Sprintf("Unknown builtin step: %s\n", a.Name)
	return res
}

func (w *currentWorker) runPlugin(ctx context.Context, a *sdk.Action, pbJob sdk.PipelineBuildJob, stepOrder int) sdk.Result {

	chanRes := make(chan sdk.Result)

	go func(pbJob *sdk.PipelineBuildJob) {
		res := sdk.Result{Status: sdk.StatusSuccess.String()}

		//For the moment we consider that plugin name = action name = plugin binary file name
		pluginName := a.Name
		//The binary file has been downloaded during requirement check in /tmp
		pluginBinary := path.Join(os.TempDir(), a.Name)

		var tlsskipverify bool
		if os.Getenv("CDS_SKIP_VERIFY") != "" {
			tlsskipverify = true
		}

		//TODO: cancel the plugin

		//Create the rpc server
		pluginClient := plugin.NewClient(pluginName, pluginBinary, w.id, w.apiEndpoint, tlsskipverify)
		defer pluginClient.Kill()

		//Get the plugin interface
		_plugin, err := pluginClient.Instance()
		if err != nil {
			result := sdk.Result{
				Status: sdk.StatusFail.String(),
				Reason: fmt.Sprintf("Unable to init plugin %s: %s\n", pluginName, err),
			}
			w.sendLog(pbJob.ID, result.Reason, pbJob.PipelineBuildID, stepOrder, false)
			chanRes <- result
		}

		//Manage all parameters
		pluginArgs := plugin.Arguments{
			Data: map[string]string{},
		}
		for _, p := range a.Parameters {
			pluginArgs.Data[p.Name] = p.Value
		}
		for _, p := range pbJob.Parameters {
			pluginArgs.Data[p.Name] = p.Value
		}

		//Call the Run function on the plugin interface
		pluginAction := plugin.Job{
			IDPipelineBuild:    pbJob.PipelineBuildID,
			IDPipelineJobBuild: pbJob.ID,
			OrderStep:          stepOrder,
			Args:               pluginArgs,
		}

		pluginResult := _plugin.Run(pluginAction)

		if pluginResult == plugin.Success {
			res.Status = sdk.StatusSuccess.String()
		}

		chanRes <- res
	}(&pbJob)

	for {
		select {
		case <-ctx.Done():
			log.Error("CDS Worker execution canceled: %v", ctx.Err())
			w.sendLog(pbJob.ID, "CDS Worker execution canceled\n", pbJob.PipelineBuildID, stepOrder, false)
			return sdk.Result{
				Status: sdk.StatusFail.String(),
				Reason: "CDS Worker execution canceled",
			}

		case res := <-chanRes:
			return res
		}
	}
}
<commit_msg>fix (worker): return fail (#727)<commit_after>package main

import (
	"context"
	"fmt"
	"os"
	"path"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
	"github.com/ovh/cds/sdk/plugin"
)

func (w *currentWorker) runBuiltin(ctx context.Context, a *sdk.Action, pbJob sdk.PipelineBuildJob, stepOrder int) sdk.Result {
	defer w.drainLogsAndCloseLogger(ctx)

	res := sdk.Result{Status: sdk.StatusFail.String()}
	switch a.Name {
	case sdk.ArtifactUpload:
		filePattern, tag := getArtifactParams(a)
		return w.runArtifactUpload(ctx, filePattern, tag, pbJob, stepOrder)
	case sdk.ArtifactDownload:
		return w.runArtifactDownload(ctx, a, pbJob, stepOrder)
	case sdk.ScriptAction:
		return w.runScriptAction(ctx, a, pbJob, stepOrder)
	case sdk.JUnitAction:
		return w.runParseJunitTestResultAction(ctx, a, pbJob, stepOrder)
	case sdk.GitCloneAction:
		return w.runGitClone(ctx, a, pbJob, stepOrder)
	}
	res.Reason = fmt.Sprintf("Unknown builtin step: %s\n", a.Name)
	return res
}

func (w *currentWorker) runPlugin(ctx context.Context, a *sdk.Action, pbJob sdk.PipelineBuildJob, stepOrder int) sdk.Result {

	chanRes := make(chan sdk.Result)

	go func(pbJob *sdk.PipelineBuildJob) {
		res := sdk.Result{Status: sdk.StatusFail.String()}

		//For the moment we consider that plugin name = action name = plugin binary file name
		pluginName := a.Name
		//The binary file has been downloaded during requirement check in /tmp
		pluginBinary := path.Join(os.TempDir(), a.Name)

		var tlsskipverify bool
		if os.Getenv("CDS_SKIP_VERIFY") != "" {
			tlsskipverify = true
		}

		//TODO: cancel the plugin

		//Create the rpc server
		pluginClient := plugin.NewClient(pluginName, pluginBinary, w.id, w.apiEndpoint, tlsskipverify)
		defer pluginClient.Kill()

		//Get the plugin interface
		_plugin, err := pluginClient.Instance()
		if err != nil {
			result := sdk.Result{
				Status: sdk.StatusFail.String(),
				Reason: fmt.Sprintf("Unable to init plugin %s: %s\n", pluginName, err),
			}
			w.sendLog(pbJob.ID, result.Reason, pbJob.PipelineBuildID, stepOrder, false)
			chanRes <- result
		}

		//Manage all parameters
		pluginArgs := plugin.Arguments{
			Data: map[string]string{},
		}
		for _, p := range a.Parameters {
			pluginArgs.Data[p.Name] = p.Value
		}
		for _, p := range pbJob.Parameters {
			pluginArgs.Data[p.Name] = p.Value
		}

		//Call the Run function on the plugin interface
		pluginAction := plugin.Job{
			IDPipelineBuild:    pbJob.PipelineBuildID,
			IDPipelineJobBuild: pbJob.ID,
			OrderStep:          stepOrder,
			Args:               pluginArgs,
		}

		pluginResult := _plugin.Run(pluginAction)

		if pluginResult == plugin.Success {
			res.Status = sdk.StatusSuccess.String()
		}

		chanRes <- res
	}(&pbJob)

	for {
		select {
		case <-ctx.Done():
			log.Error("CDS Worker execution canceled: %v", ctx.Err())
			w.sendLog(pbJob.ID, "CDS Worker execution canceled\n", pbJob.PipelineBuildID, stepOrder, false)
			return sdk.Result{
				Status: sdk.StatusFail.String(),
				Reason: "CDS Worker execution canceled",
			}

		case res := <-chanRes:
			return res
		}
	}
}
<|endoftext|>