<commit_before>package git

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"time"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/go-codec/codec"
)

type ServerResponseRepo struct {
	TeamID                keybase1.TeamID               `json:"team_id"`
	RepoID                keybase1.RepoID               `json:"repo_id"`
	CTime                 time.Time                     `json:"ctime"`
	MTime                 time.Time                     `json:"mtime"`
	EncryptedMetadata     string                        `json:"encrypted_metadata"`
	EncryptionVersion     int                           `json:"encryption_version"`
	Nonce                 string                        `json:"nonce"`
	KeyGeneration         keybase1.PerTeamKeyGeneration `json:"key_generation"`
	LastModifyingUsername string                        `json:"last_writer_username"`
	LastModifyingDeviceID keybase1.DeviceID             `json:"last_writer_device_id"`
}

type ServerResponse struct {
	Repos  []ServerResponseRepo `json:"repos"`
	Status libkb.AppStatus      `json:"status"`
}

var _ libkb.APIResponseWrapper = (*ServerResponse)(nil)

// For GetDecode.
func (r *ServerResponse) GetAppStatus() *libkb.AppStatus {
	return &r.Status
}

// Implicit teams need to be converted back into the folder that matches their
// display name. Regular teams become a regular team folder.
func folderFromTeamID(ctx context.Context, g *libkb.GlobalContext, teamID keybase1.TeamID) (keybase1.Folder, error) {
	team, err := teams.Load(ctx, g, keybase1.LoadTeamArg{
		ID: teamID,
	})
	if err != nil {
		return keybase1.Folder{}, err
	}
	if team.IsImplicit() {
		// TODO: This function doesn't currently support conflict info.
		name, err := team.ImplicitTeamDisplayNameString(ctx)
		if err != nil {
			return keybase1.Folder{}, err
		}
		var folderType keybase1.FolderType
		if team.IsPublic() {
			folderType = keybase1.FolderType_PUBLIC
		} else {
			folderType = keybase1.FolderType_PRIVATE
		}
		return keybase1.Folder{
			Name:       name,
			FolderType: folderType,
			Private:    !team.IsPublic(),
		}, nil
	} else {
		return keybase1.Folder{
			Name:       team.Name().String(),
			FolderType: keybase1.FolderType_TEAM,
			Private:    !team.IsPublic(),
		}, nil
	}
}

func getMetadataInner(ctx context.Context, g *libkb.GlobalContext, folder *keybase1.Folder) ([]keybase1.GitRepoResult, error) {
	teamer := NewTeamer(g)
	cryptoer := NewCrypto(g)

	apiArg := libkb.APIArg{
		Endpoint:    "kbfs/git/team/get",
		SessionType: libkb.APISessionTypeREQUIRED,
		NetContext:  ctx,
		Args:        libkb.HTTPArgs{
		// a limit parameter exists, default 100, and we don't currently set it
		},
	}

	// The team_id parameter is optional. Add it in if the caller supplied it.
	if folder != nil {
		if folder.FolderType != keybase1.FolderType_TEAM {
			return nil, errors.New("git repo folders must be team folders")
		}
		teamIDVis, err := teamer.LookupOrCreate(ctx, *folder)
		if err != nil {
			return nil, err
		}
		apiArg.Args["team_id"] = libkb.S{Val: string(teamIDVis.TeamID)}
	}
	var serverResponse ServerResponse
	err := g.GetAPI().GetDecode(apiArg, &serverResponse)
	if err != nil {
		return nil, err
	}

	// Initializing the results list to non-nil means that we end up seeing
	// "[]" instead of "null" in the final JSON output on the CLI, which is
	// preferable.
	resultList := []keybase1.GitRepoResult{}
	for _, responseRepo := range serverResponse.Repos {
		// If the folder was passed in, use it. Otherwise, load the team to
		// figure it out.
		var repoFolder keybase1.Folder
		if folder != nil {
			repoFolder = *folder
		} else {
			repoFolder, err = folderFromTeamID(ctx, g, responseRepo.TeamID)
			if err != nil {
				return nil, err
			}
		}

		teamIDVis := keybase1.TeamIDWithVisibility{
			TeamID: responseRepo.TeamID,
		}
		if repoFolder.Private {
			teamIDVis.Visibility = keybase1.TLFVisibility_PRIVATE
		} else {
			teamIDVis.Visibility = keybase1.TLFVisibility_PUBLIC
		}

		ciphertext, err := base64.StdEncoding.DecodeString(responseRepo.EncryptedMetadata)
		if err != nil {
			return nil, err
		}

		nonceSlice, err := base64.StdEncoding.DecodeString(responseRepo.Nonce)
		if err != nil {
			return nil, err
		}
		if len(nonceSlice) != len(keybase1.BoxNonce{}) {
			return nil, fmt.Errorf("expected a nonce of length %d, found %d", len(keybase1.BoxNonce{}), len(nonceSlice))
		}
		var nonce keybase1.BoxNonce
		copy(nonce[:], nonceSlice)

		encryptedMetadata := keybase1.EncryptedGitMetadata{
			V:   responseRepo.EncryptionVersion,
			E:   ciphertext,
			N:   nonce,
			Gen: responseRepo.KeyGeneration,
		}
		msgpackPlaintext, err := cryptoer.Unbox(ctx, teamIDVis, &encryptedMetadata)
		if err != nil {
			return nil, err
		}

		var localMetadataVersioned keybase1.GitLocalMetadataVersioned
		mh := codec.MsgpackHandle{WriteExt: true}
		dec := codec.NewDecoderBytes(msgpackPlaintext, &mh)
		err = dec.Decode(&localMetadataVersioned)
		if err != nil {
			return nil, err
		}

		// Translate back from GitLocalMetadataVersioned to the decoupled type
		// that we use for local RPC.
		version, err := localMetadataVersioned.Version()
		if err != nil {
			return nil, err
		}
		var localMetadata keybase1.GitLocalMetadata
		switch version {
		case keybase1.GitLocalMetadataVersion_V1:
			localMetadata = keybase1.GitLocalMetadata{
				RepoName: localMetadataVersioned.V1().RepoName,
			}
		default:
			return nil, fmt.Errorf("unrecognized variant of GitLocalMetadataVersioned: %#v", version)
		}

		resultList = append(resultList, keybase1.GitRepoResult{
			Folder:        repoFolder,
			RepoID:        responseRepo.RepoID,
			LocalMetadata: localMetadata,
			ServerMetadata: keybase1.GitServerMetadata{
				Ctime: keybase1.ToTime(responseRepo.CTime),
				Mtime: keybase1.ToTime(responseRepo.MTime),
				LastModifyingUsername: responseRepo.LastModifyingUsername,
				LastModifyingDeviceID: responseRepo.LastModifyingDeviceID,
			},
		})
	}
	return resultList, nil
}

func GetMetadata(ctx context.Context, g *libkb.GlobalContext, folder keybase1.Folder) ([]keybase1.GitRepoResult, error) {
	return getMetadataInner(ctx, g, &folder)
}

func GetAllMetadata(ctx context.Context, g *libkb.GlobalContext) ([]keybase1.GitRepoResult, error) {
	return getMetadataInner(ctx, g, nil)
}
<commit_msg>Fix golint warning<commit_after>package git

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"time"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/go-codec/codec"
)

type ServerResponseRepo struct {
	TeamID                keybase1.TeamID               `json:"team_id"`
	RepoID                keybase1.RepoID               `json:"repo_id"`
	CTime                 time.Time                     `json:"ctime"`
	MTime                 time.Time                     `json:"mtime"`
	EncryptedMetadata     string                        `json:"encrypted_metadata"`
	EncryptionVersion     int                           `json:"encryption_version"`
	Nonce                 string                        `json:"nonce"`
	KeyGeneration         keybase1.PerTeamKeyGeneration `json:"key_generation"`
	LastModifyingUsername string                        `json:"last_writer_username"`
	LastModifyingDeviceID keybase1.DeviceID             `json:"last_writer_device_id"`
}

type ServerResponse struct {
	Repos  []ServerResponseRepo `json:"repos"`
	Status libkb.AppStatus      `json:"status"`
}

var _ libkb.APIResponseWrapper = (*ServerResponse)(nil)

// For GetDecode.
func (r *ServerResponse) GetAppStatus() *libkb.AppStatus {
	return &r.Status
}

// Implicit teams need to be converted back into the folder that matches their
// display name. Regular teams become a regular team folder.
func folderFromTeamID(ctx context.Context, g *libkb.GlobalContext, teamID keybase1.TeamID) (keybase1.Folder, error) {
	team, err := teams.Load(ctx, g, keybase1.LoadTeamArg{
		ID: teamID,
	})
	if err != nil {
		return keybase1.Folder{}, err
	}
	if team.IsImplicit() {
		// TODO: This function doesn't currently support conflict info.
		name, err := team.ImplicitTeamDisplayNameString(ctx)
		if err != nil {
			return keybase1.Folder{}, err
		}
		var folderType keybase1.FolderType
		if team.IsPublic() {
			folderType = keybase1.FolderType_PUBLIC
		} else {
			folderType = keybase1.FolderType_PRIVATE
		}
		return keybase1.Folder{
			Name:       name,
			FolderType: folderType,
			Private:    !team.IsPublic(),
		}, nil
	}
	return keybase1.Folder{
		Name:       team.Name().String(),
		FolderType: keybase1.FolderType_TEAM,
		Private:    !team.IsPublic(),
	}, nil
}

func getMetadataInner(ctx context.Context, g *libkb.GlobalContext, folder *keybase1.Folder) ([]keybase1.GitRepoResult, error) {
	teamer := NewTeamer(g)
	cryptoer := NewCrypto(g)

	apiArg := libkb.APIArg{
		Endpoint:    "kbfs/git/team/get",
		SessionType: libkb.APISessionTypeREQUIRED,
		NetContext:  ctx,
		Args:        libkb.HTTPArgs{
		// a limit parameter exists, default 100, and we don't currently set it
		},
	}

	// The team_id parameter is optional. Add it in if the caller supplied it.
	if folder != nil {
		if folder.FolderType != keybase1.FolderType_TEAM {
			return nil, errors.New("git repo folders must be team folders")
		}
		teamIDVis, err := teamer.LookupOrCreate(ctx, *folder)
		if err != nil {
			return nil, err
		}
		apiArg.Args["team_id"] = libkb.S{Val: string(teamIDVis.TeamID)}
	}
	var serverResponse ServerResponse
	err := g.GetAPI().GetDecode(apiArg, &serverResponse)
	if err != nil {
		return nil, err
	}

	// Initializing the results list to non-nil means that we end up seeing
	// "[]" instead of "null" in the final JSON output on the CLI, which is
	// preferable.
	resultList := []keybase1.GitRepoResult{}
	for _, responseRepo := range serverResponse.Repos {
		// If the folder was passed in, use it. Otherwise, load the team to
		// figure it out.
		var repoFolder keybase1.Folder
		if folder != nil {
			repoFolder = *folder
		} else {
			repoFolder, err = folderFromTeamID(ctx, g, responseRepo.TeamID)
			if err != nil {
				return nil, err
			}
		}

		teamIDVis := keybase1.TeamIDWithVisibility{
			TeamID: responseRepo.TeamID,
		}
		if repoFolder.Private {
			teamIDVis.Visibility = keybase1.TLFVisibility_PRIVATE
		} else {
			teamIDVis.Visibility = keybase1.TLFVisibility_PUBLIC
		}

		ciphertext, err := base64.StdEncoding.DecodeString(responseRepo.EncryptedMetadata)
		if err != nil {
			return nil, err
		}

		nonceSlice, err := base64.StdEncoding.DecodeString(responseRepo.Nonce)
		if err != nil {
			return nil, err
		}
		if len(nonceSlice) != len(keybase1.BoxNonce{}) {
			return nil, fmt.Errorf("expected a nonce of length %d, found %d", len(keybase1.BoxNonce{}), len(nonceSlice))
		}
		var nonce keybase1.BoxNonce
		copy(nonce[:], nonceSlice)

		encryptedMetadata := keybase1.EncryptedGitMetadata{
			V:   responseRepo.EncryptionVersion,
			E:   ciphertext,
			N:   nonce,
			Gen: responseRepo.KeyGeneration,
		}
		msgpackPlaintext, err := cryptoer.Unbox(ctx, teamIDVis, &encryptedMetadata)
		if err != nil {
			return nil, err
		}

		var localMetadataVersioned keybase1.GitLocalMetadataVersioned
		mh := codec.MsgpackHandle{WriteExt: true}
		dec := codec.NewDecoderBytes(msgpackPlaintext, &mh)
		err = dec.Decode(&localMetadataVersioned)
		if err != nil {
			return nil, err
		}

		// Translate back from GitLocalMetadataVersioned to the decoupled type
		// that we use for local RPC.
		version, err := localMetadataVersioned.Version()
		if err != nil {
			return nil, err
		}
		var localMetadata keybase1.GitLocalMetadata
		switch version {
		case keybase1.GitLocalMetadataVersion_V1:
			localMetadata = keybase1.GitLocalMetadata{
				RepoName: localMetadataVersioned.V1().RepoName,
			}
		default:
			return nil, fmt.Errorf("unrecognized variant of GitLocalMetadataVersioned: %#v", version)
		}

		resultList = append(resultList, keybase1.GitRepoResult{
			Folder:        repoFolder,
			RepoID:        responseRepo.RepoID,
			LocalMetadata: localMetadata,
			ServerMetadata: keybase1.GitServerMetadata{
				Ctime: keybase1.ToTime(responseRepo.CTime),
				Mtime: keybase1.ToTime(responseRepo.MTime),
				LastModifyingUsername: responseRepo.LastModifyingUsername,
				LastModifyingDeviceID: responseRepo.LastModifyingDeviceID,
			},
		})
	}
	return resultList, nil
}

func GetMetadata(ctx context.Context, g *libkb.GlobalContext, folder keybase1.Folder) ([]keybase1.GitRepoResult, error) {
	return getMetadataInner(ctx, g, &folder)
}

func GetAllMetadata(ctx context.Context, g *libkb.GlobalContext) ([]keybase1.GitRepoResult, error) {
	return getMetadataInner(ctx, g, nil)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goapp

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"time"

	"appengine/datastore"
	"github.com/MiniProfiler/go/miniprofiler"
	mpg "github.com/MiniProfiler/go/miniprofiler_gae"
	"github.com/gorilla/mux"
	"github.com/mjibson/goon"
)

var router = new(mux.Router)
var templates *template.Template

func init() {
	var err error

	if templates, err = template.New("").Funcs(funcs).
		ParseFiles(
		"templates/base.html",
		"templates/admin-all-feeds.html",
		"templates/admin-date-formats.html",
		"templates/admin-feed.html",
		"templates/admin-stats.html",
	); err != nil {
		log.Fatal(err)
	}

	router.Handle("/", mpg.NewHandler(Main)).Name("main")
	router.Handle("/login/google", mpg.NewHandler(LoginGoogle)).Name("login-google")
	router.Handle("/logout", mpg.NewHandler(Logout)).Name("logout")
	router.Handle("/oauth2callback", mpg.NewHandler(Oauth2Callback)).Name("oauth2callback")
	router.Handle("/tasks/import-opml", mpg.NewHandler(ImportOpmlTask)).Name("import-opml-task")
	router.Handle("/tasks/import-reader", mpg.NewHandler(ImportReaderTask)).Name("import-reader-task")
	router.Handle("/tasks/update-feed", mpg.NewHandler(UpdateFeed)).Name("update-feed")
	router.Handle("/tasks/update-feeds", mpg.NewHandler(UpdateFeeds)).Name("update-feeds")
	router.Handle("/tasks/subscribe-feed", mpg.NewHandler(SubscribeFeed)).Name("subscribe-feed")
	router.Handle("/push/{feed}", mpg.NewHandler(SubscribeCallback)).Name("subscribe-callback")
	router.Handle("/user/add-subscription", mpg.NewHandler(AddSubscription)).Name("add-subscription")
	router.Handle("/user/clear-feeds", mpg.NewHandler(ClearFeeds)).Name("clear-feeds")
	router.Handle("/user/delete-account", mpg.NewHandler(DeleteAccount)).Name("delete-account")
	router.Handle("/user/export-opml", mpg.NewHandler(ExportOpml)).Name("export-opml")
	router.Handle("/user/get-contents", mpg.NewHandler(GetContents)).Name("get-contents")
	router.Handle("/user/get-feed", mpg.NewHandler(GetFeed)).Name("get-feed")
	router.Handle("/user/import/opml", mpg.NewHandler(ImportOpml)).Name("import-opml")
	router.Handle("/user/import/reader", mpg.NewHandler(ImportReader)).Name("import-reader")
	router.Handle("/user/list-feeds", mpg.NewHandler(ListFeeds)).Name("list-feeds")
	router.Handle("/user/mark-all-read", mpg.NewHandler(MarkAllRead)).Name("mark-all-read")
	router.Handle("/user/mark-read", mpg.NewHandler(MarkRead)).Name("mark-read")
	router.Handle("/user/save-options", mpg.NewHandler(SaveOptions)).Name("save-options")
	router.Handle("/user/upload-opml", mpg.NewHandler(UploadOpml)).Name("upload-opml")

	router.Handle("/admin/all-feeds", mpg.NewHandler(AllFeeds)).Name("all-feeds")
	router.Handle("/admin/all-feeds-opml", mpg.NewHandler(AllFeedsOpml)).Name("all-feeds-opml")
	router.Handle("/admin/date-formats", mpg.NewHandler(AdminDateFormats)).Name("admin-date-formats")
	router.Handle("/admin/feed", mpg.NewHandler(AdminFeed)).Name("admin-feed")
	router.Handle("/admin/stats", mpg.NewHandler(AdminStats)).Name("admin-stats")
	router.Handle("/admin/update-feed", mpg.NewHandler(AdminUpdateFeed)).Name("admin-update-feed")
	router.Handle("/_ah/start", mpg.NewHandler(BackendStart))
	router.Handle("/_ah/stop", mpg.NewHandler(BackendStop))

	http.Handle("/", router)

	miniprofiler.ShowControls = true
	miniprofiler.StartHidden = true
	miniprofiler.ToggleShortcut = "Alt+C"
}

func Main(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	if err := templates.ExecuteTemplate(w, "base.html", includes(c, w, r)); err != nil {
		serveError(w, err)
	}
}

func addFeed(c mpg.Context, userid string, outline *OpmlOutline) error {
	gn := goon.FromContext(c)
	o := outline.Outline[0]
	c.Infof("adding feed %v to user %s", o.XmlUrl, userid)
	fu, ferr := url.Parse(o.XmlUrl)
	if ferr != nil {
		return ferr
	}
	fu.Fragment = ""
	o.XmlUrl = fu.String()

	f := Feed{Url: o.XmlUrl}
	if err := gn.Get(&f); err == datastore.ErrNoSuchEntity {
		if feed, stories := fetchFeed(c, o.XmlUrl, o.XmlUrl); feed == nil {
			return fmt.Errorf("could not add feed %s", o.XmlUrl)
		} else {
			f = *feed
			f.Updated = time.Time{}
			f.Checked = f.Updated
			f.NextUpdate = f.Updated
			gn.Put(&f)
			if err := updateFeed(c, f.Url, feed, stories); err != nil {
				return err
			}

			o.XmlUrl = feed.Url
			o.HtmlUrl = feed.Link
			if o.Title == "" {
				o.Title = feed.Title
			}
		}
	} else if err != nil {
		return err
	} else {
		o.HtmlUrl = f.Link
		if o.Title == "" {
			o.Title = f.Title
		}
	}
	o.Text = ""

	return nil
}

func mergeUserOpml(ud *UserData, outlines ...*OpmlOutline) {
	var fs Opml
	json.Unmarshal(ud.Opml, &fs)
	urls := make(map[string]bool)

	for _, o := range fs.Outline {
		if o.XmlUrl != "" {
			urls[o.XmlUrl] = true
		} else {
			for _, so := range o.Outline {
				urls[so.XmlUrl] = true
			}
		}
	}

	mergeOutline := func(label string, outline *OpmlOutline) {
		if _, present := urls[outline.XmlUrl]; present {
			return
		} else {
			urls[outline.XmlUrl] = true

			if label == "" {
				fs.Outline = append(fs.Outline, outline)
			} else {
				done := false
				for _, ol := range fs.Outline {
					if ol.Title == label && ol.XmlUrl == "" {
						ol.Outline = append(ol.Outline, outline)
						done = true
						break
					}
				}
				if !done {
					fs.Outline = append(fs.Outline, &OpmlOutline{
						Title:   label,
						Outline: []*OpmlOutline{outline},
					})
				}
			}
		}
	}

	for _, outline := range outlines {
		if outline.XmlUrl != "" {
			mergeOutline("", outline)
		} else {
			for _, o := range outline.Outline {
				mergeOutline(outline.Title, o)
			}
		}
	}

	ud.Opml, _ = json.Marshal(&fs)
}

// Below is from David Symonds; will be in next app engine release

// Timeout returns a replacement context that uses d as the default API RPC timeout.
func Timeout(c appengine.Context, d time.Duration) Context {
	return &timeoutContext{
		Context: c,
		d:       d,
	}
}

type timeoutContext struct {
	appengine.Context
	d time.Duration
}

func (t *timeoutContext) Call(service, method string, in, out appengine_internal.ProtoMessage, opts *appengine_internal.CallOptions) error {
	// Only affect calls that don't have a timeout.
	if opts == nil || opts.Timeout == 0 {
		newOpts := new(appengine_internal.CallOptions)
		if opts != nil {
			*newOpts = *opts
		}
		newOpts.Timeout = t.d
		opts = newOpts
	}
	return t.Context.Call(service, method, in, out, opts)
}
<commit_msg>Fixes<commit_after>/*
 * Copyright (c) 2013 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goapp

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine_internal"
	"github.com/MiniProfiler/go/miniprofiler"
	mpg "github.com/MiniProfiler/go/miniprofiler_gae"
	"github.com/gorilla/mux"
	"github.com/mjibson/goon"
)

var router = new(mux.Router)
var templates *template.Template

func init() {
	var err error

	if templates, err = template.New("").Funcs(funcs).
		ParseFiles(
		"templates/base.html",
		"templates/admin-all-feeds.html",
		"templates/admin-date-formats.html",
		"templates/admin-feed.html",
		"templates/admin-stats.html",
	); err != nil {
		log.Fatal(err)
	}

	router.Handle("/", mpg.NewHandler(Main)).Name("main")
	router.Handle("/login/google", mpg.NewHandler(LoginGoogle)).Name("login-google")
	router.Handle("/logout", mpg.NewHandler(Logout)).Name("logout")
	router.Handle("/oauth2callback", mpg.NewHandler(Oauth2Callback)).Name("oauth2callback")
	router.Handle("/tasks/import-opml", mpg.NewHandler(ImportOpmlTask)).Name("import-opml-task")
	router.Handle("/tasks/import-reader", mpg.NewHandler(ImportReaderTask)).Name("import-reader-task")
	router.Handle("/tasks/update-feed", mpg.NewHandler(UpdateFeed)).Name("update-feed")
	router.Handle("/tasks/update-feeds", mpg.NewHandler(UpdateFeeds)).Name("update-feeds")
	router.Handle("/tasks/subscribe-feed", mpg.NewHandler(SubscribeFeed)).Name("subscribe-feed")
	router.Handle("/push/{feed}", mpg.NewHandler(SubscribeCallback)).Name("subscribe-callback")
	router.Handle("/user/add-subscription", mpg.NewHandler(AddSubscription)).Name("add-subscription")
	router.Handle("/user/clear-feeds", mpg.NewHandler(ClearFeeds)).Name("clear-feeds")
	router.Handle("/user/delete-account", mpg.NewHandler(DeleteAccount)).Name("delete-account")
	router.Handle("/user/export-opml", mpg.NewHandler(ExportOpml)).Name("export-opml")
	router.Handle("/user/get-contents", mpg.NewHandler(GetContents)).Name("get-contents")
	router.Handle("/user/get-feed", mpg.NewHandler(GetFeed)).Name("get-feed")
	router.Handle("/user/import/opml", mpg.NewHandler(ImportOpml)).Name("import-opml")
	router.Handle("/user/import/reader", mpg.NewHandler(ImportReader)).Name("import-reader")
	router.Handle("/user/list-feeds", mpg.NewHandler(ListFeeds)).Name("list-feeds")
	router.Handle("/user/mark-all-read", mpg.NewHandler(MarkAllRead)).Name("mark-all-read")
	router.Handle("/user/mark-read", mpg.NewHandler(MarkRead)).Name("mark-read")
	router.Handle("/user/save-options", mpg.NewHandler(SaveOptions)).Name("save-options")
	router.Handle("/user/upload-opml", mpg.NewHandler(UploadOpml)).Name("upload-opml")

	router.Handle("/admin/all-feeds", mpg.NewHandler(AllFeeds)).Name("all-feeds")
	router.Handle("/admin/all-feeds-opml", mpg.NewHandler(AllFeedsOpml)).Name("all-feeds-opml")
	router.Handle("/admin/date-formats", mpg.NewHandler(AdminDateFormats)).Name("admin-date-formats")
	router.Handle("/admin/feed", mpg.NewHandler(AdminFeed)).Name("admin-feed")
	router.Handle("/admin/stats", mpg.NewHandler(AdminStats)).Name("admin-stats")
	router.Handle("/admin/update-feed", mpg.NewHandler(AdminUpdateFeed)).Name("admin-update-feed")
	router.Handle("/_ah/start", mpg.NewHandler(BackendStart))
	router.Handle("/_ah/stop", mpg.NewHandler(BackendStop))

	http.Handle("/", router)

	miniprofiler.ShowControls = true
	miniprofiler.StartHidden = true
	miniprofiler.ToggleShortcut = "Alt+C"
}

func Main(c mpg.Context, w http.ResponseWriter, r *http.Request) {
	if err := templates.ExecuteTemplate(w, "base.html", includes(c, w, r)); err != nil {
		serveError(w, err)
	}
}

func addFeed(c mpg.Context, userid string, outline *OpmlOutline) error {
	gn := goon.FromContext(c)
	o := outline.Outline[0]
	c.Infof("adding feed %v to user %s", o.XmlUrl, userid)
	fu, ferr := url.Parse(o.XmlUrl)
	if ferr != nil {
		return ferr
	}
	fu.Fragment = ""
	o.XmlUrl = fu.String()

	f := Feed{Url: o.XmlUrl}
	if err := gn.Get(&f); err == datastore.ErrNoSuchEntity {
		if feed, stories := fetchFeed(c, o.XmlUrl, o.XmlUrl); feed == nil {
			return fmt.Errorf("could not add feed %s", o.XmlUrl)
		} else {
			f = *feed
			f.Updated = time.Time{}
			f.Checked = f.Updated
			f.NextUpdate = f.Updated
			gn.Put(&f)
			if err := updateFeed(c, f.Url, feed, stories); err != nil {
				return err
			}

			o.XmlUrl = feed.Url
			o.HtmlUrl = feed.Link
			if o.Title == "" {
				o.Title = feed.Title
			}
		}
	} else if err != nil {
		return err
	} else {
		o.HtmlUrl = f.Link
		if o.Title == "" {
			o.Title = f.Title
		}
	}
	o.Text = ""

	return nil
}

func mergeUserOpml(ud *UserData, outlines ...*OpmlOutline) {
	var fs Opml
	json.Unmarshal(ud.Opml, &fs)
	urls := make(map[string]bool)

	for _, o := range fs.Outline {
		if o.XmlUrl != "" {
			urls[o.XmlUrl] = true
		} else {
			for _, so := range o.Outline {
				urls[so.XmlUrl] = true
			}
		}
	}

	mergeOutline := func(label string, outline *OpmlOutline) {
		if _, present := urls[outline.XmlUrl]; present {
			return
		} else {
			urls[outline.XmlUrl] = true

			if label == "" {
				fs.Outline = append(fs.Outline, outline)
			} else {
				done := false
				for _, ol := range fs.Outline {
					if ol.Title == label && ol.XmlUrl == "" {
						ol.Outline = append(ol.Outline, outline)
						done = true
						break
					}
				}
				if !done {
					fs.Outline = append(fs.Outline, &OpmlOutline{
						Title:   label,
						Outline: []*OpmlOutline{outline},
					})
				}
			}
		}
	}

	for _, outline := range outlines {
		if outline.XmlUrl != "" {
			mergeOutline("", outline)
		} else {
			for _, o := range outline.Outline {
				mergeOutline(outline.Title, o)
			}
		}
	}

	ud.Opml, _ = json.Marshal(&fs)
}

// Below is from David Symonds; will be in next app engine release

// Timeout returns a replacement context that uses d as the default API RPC timeout.
func Timeout(c appengine.Context, d time.Duration) appengine.Context {
	return &timeoutContext{
		Context: c,
		d:       d,
	}
}

type timeoutContext struct {
	appengine.Context
	d time.Duration
}

func (t *timeoutContext) Call(service, method string, in, out appengine_internal.ProtoMessage, opts *appengine_internal.CallOptions) error {
	// Only affect calls that don't have a timeout.
	if opts == nil || opts.Timeout == 0 {
		newOpts := new(appengine_internal.CallOptions)
		if opts != nil {
			*newOpts = *opts
		}
		newOpts.Timeout = t.d
		opts = newOpts
	}
	return t.Context.Call(service, method, in, out, opts)
}
<|endoftext|>


<commit_before>package gobls

import (
	"bufio"
	"bytes"
	"testing"
)

const (
	lineCount  = 100
	lineLength = 4096
)

func makeBuffer(lineCount, lineLength int) *bytes.Buffer {
	buf := make([]byte, 0, lineCount*(lineLength+2))
	bb := bytes.NewBuffer(buf)
	for line := 0; line < lineCount; line++ {
		for i := 0; i < lineLength; i++ {
			bb.WriteByte('a')
		}
		bb.WriteString("\r\n")
	}
	return bb
}

func TestNoEOF(t *testing.T) {
	bb := bytes.NewBufferString("")
	s := NewScanner(bb)
	for s.Scan() {
		t.Errorf("Actual: scan returned true; Expected: false")
	}
	if s.Err() != nil {
		t.Errorf("Actual: %#v; Expected: %#v", s.Err(), nil)
	}
}

func TestSequencesThroughEntireBuffer(t *testing.T) {
	bb := bytes.NewBufferString("flubber\nblubber\nfoo")
	s := NewScanner(bb)
	expectedLines := []string{"flubber", "blubber", "foo"}
	actualLines := make([]string, 0)
	for s.Scan() {
		actualLines = append(actualLines, s.String())
	}
	if s.Err() != nil {
		t.Errorf("Actual: %#v; Expected: %#v", s.Err(), nil)
	}
	if len(actualLines) != len(expectedLines) {
		t.Fatalf("Actual: %#v; Expected: %#v", len(actualLines), len(expectedLines))
	}
	for i := 0; i < len(expectedLines); i++ {
		if actualLines[i] != expectedLines[i] {
			t.Errorf("Actual: %#v; Expected: %#v",
				actualLines[i], expectedLines[i])
		}
	}
}

func TestVeryLargeLinesRequireSingleInvocation(t *testing.T) {
	r := makeBuffer(1, bufio.MaxScanTokenSize+5)
	line := r.String()
	line = line[:len(line)-2] // trim CRLF

	s := NewScanner(r)
	lines := make([]string, 0, 1)
	for s.Scan() {
		lines = append(lines, s.String())
	}
	if s.Err() != nil {
		t.Errorf("Actual: %#v; Expected: %#v", s.Err(), nil)
	}
	if len(lines) != 1 {
		t.Fatalf("Actual: %#v; Expected: %#v", len(lines), 1)
	}
	if lines[0] != line {
		t.Errorf("Actual: %#v; Expected: %#v", lines[0], line)
	}
}

func BenchmarkBufioScanner(b *testing.B) {
	master := makeBuffer(lineCount, lineLength)
	initial := master.Bytes()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		s := bufio.NewScanner(bytes.NewBuffer(initial))
		for s.Scan() {
			_ = s.Text()
		}
	}
}

func BenchmarkGobls(b *testing.B) {
	master := makeBuffer(lineCount, lineLength)
	initial := master.Bytes()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		s := NewScanner(bytes.NewBuffer(initial))
		for s.Scan() {
			_ = s.String()
		}
	}
}
<commit_msg>test forces evaluation of bufio.Scanner's Text() method, and gobls.Scanner's String() method<commit_after>package gobls

import (
	"bufio"
	"bytes"
	"testing"
)

const (
	lineCount  = 100
	lineLength = 1024
)

func makeBuffer(lineCount, lineLength int) *bytes.Buffer {
	buf := make([]byte, 0, lineCount*(lineLength+2))
	bb := bytes.NewBuffer(buf)
	for line := 0; line < lineCount; line++ {
		for i := 0; i < lineLength; i++ {
			bb.WriteByte('a')
		}
		bb.WriteString("\r\n")
	}
	return bb
}

func TestNoEOF(t *testing.T) {
	bb := bytes.NewBufferString("")
	s := NewScanner(bb)
	for s.Scan() {
		t.Errorf("Actual: scan returned true; Expected: false")
	}
	if s.Err() != nil {
		t.Errorf("Actual: %#v; Expected: %#v", s.Err(), nil)
	}
}

func TestSequencesThroughEntireBuffer(t *testing.T) {
	bb := bytes.NewBufferString("flubber\nblubber\nfoo")
	s := NewScanner(bb)
	expectedLines := []string{"flubber", "blubber", "foo"}
	actualLines := make([]string, 0)
	for s.Scan() {
		actualLines = append(actualLines, s.String())
	}
	if s.Err() != nil {
		t.Errorf("Actual: %#v; Expected: %#v", s.Err(), nil)
	}
	if len(actualLines) != len(expectedLines) {
		t.Fatalf("Actual: %#v; Expected: %#v", len(actualLines), len(expectedLines))
	}
	for i := 0; i < len(expectedLines); i++ {
		if actualLines[i] != expectedLines[i] {
			t.Errorf("Actual: %#v; Expected: %#v",
				actualLines[i], expectedLines[i])
		}
	}
}

func TestVeryLargeLinesRequireSingleInvocation(t *testing.T) {
	r := makeBuffer(1, bufio.MaxScanTokenSize+5)
	line := r.String()
	line = line[:len(line)-2] // trim CRLF

	s := NewScanner(r)
	lines := make([]string, 0, 1)
	for s.Scan() {
		lines = append(lines, s.String())
	}
	if s.Err() != nil {
		t.Errorf("Actual: %#v; Expected: %#v", s.Err(), nil)
	}
	if len(lines) != 1 {
		t.Fatalf("Actual: %#v; Expected: %#v", len(lines), 1)
	}
	if lines[0] != line {
		t.Errorf("Actual: %#v; Expected: %#v", lines[0], line)
	}
}

func BenchmarkBufioScanner(b *testing.B) {
	master := makeBuffer(lineCount, lineLength)
	initial := master.Bytes()
	var line string
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		s := bufio.NewScanner(bytes.NewBuffer(initial))
		for s.Scan() {
			line = s.Text()
		}
	}
	if len(line) != lineLength {
		b.Errorf("Actual: %#v; Expected: %#v", len(line), lineLength)
	}
}

func BenchmarkGobls(b *testing.B) {
	master := makeBuffer(lineCount, lineLength)
	initial := master.Bytes()
	var line string
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		s := NewScanner(bytes.NewBuffer(initial))
		for s.Scan() {
			line = s.String()
		}
	}
	if len(line) != lineLength {
		b.Errorf("Actual: %#v; Expected: %#v", len(line), lineLength)
	}
}
<|endoftext|>


<commit_before>package gocd

import (
	"context"
	"fmt"
	"net/url"
)

// AgentsService describes the HAL _link resource for the api response object for an agent objects.
type AgentsService service

// AgentsLinks describes the HAL _link resource for the api response object for a collection of agent objects.
//go:generate gocd-response-links-generator -type=AgentsLinks,AgentLinks
type AgentsLinks struct {
	Self *url.URL `json:"self"`
	Doc  *url.URL `json:"doc"`
}

// AgentLinks describes the HAL _link resource for the api response object for a single agent object.
type AgentLinks struct {
	Self *url.URL `json:"self"`
	Doc  *url.URL `json:"doc"`
	Find *url.URL `json:"find"`
}

// AgentsResponse describes the structure of the API response when listing collections of agent object.
type AgentsResponse struct {
	Links    *AgentsLinks `json:"_links,omitempty"`
	Embedded *struct {
		Agents []*Agent `json:"agents"`
	} `json:"_embedded,omitempty"`
}

// Agent describes a single agent object.
type Agent struct {
	UUID             string        `json:"uuid"`
	Hostname         string        `json:"hostname"`
	ElasticAgentID   string        `json:"elastic_agent_id"`
	ElasticPluginID  string        `json:"elastic_plugin_id"`
	IPAddress        string        `json:"ip_address"`
	Sandbox          string        `json:"sandbox"`
	OperatingSystem  string        `json:"operating_system"`
	FreeSpace        int           `json:"free_space"`
	AgentConfigState string        `json:"agent_config_state"`
	AgentState       string        `json:"agent_state"`
	Resources        []string      `json:"resources"`
	Environments     []string      `json:"environments"`
	BuildState       string        `json:"build_state"`
	BuildDetails     *BuildDetails `json:"build_details"`
	Links            *AgentLinks   `json:"_links,omitempty"`
	client           *Client
}

// JobRunHistory retrieves the list of jobs run on this agent
func (a *Agent) JobRunHistory(ctx context.Context) ([]*Job, error) {
	jobs, _, err := a.client.Agents.JobRunHistory(ctx, a.UUID)
	if err != nil {
		return nil, err
	}

	return jobs, nil
}

// AgentUpdate describes the structure for the PUT payload when updating an agent
type AgentUpdate struct {
	Hostname         string   `json:"hostname,omitempty"`
	Resources        []string `json:"resources,omitempty"`
	Environments     []string `json:"environments,omitempty"`
	AgentConfigState string   `json:"agent_config_state,omitempty"`
}

// AgentBulkUpdate describes the structure for the PUT payload when updating multiple agents
type AgentBulkUpdate struct {
	Uuids            []string                   `json:"uuids"`
	Operations       *AgentBulkOperationsUpdate `json:"operations,omitempty"`
	AgentConfigState string                     `json:"agent_config_state,omitempty"`
}

// AgentBulkOperationsUpdate describes the structure for a single Operation in AgentBulkUpdate the PUT payload when
// updating multiple agents
type AgentBulkOperationsUpdate struct {
	Environments *AgentBulkOperationUpdate `json:"environments,omitempty"`
	Resources    *AgentBulkOperationUpdate `json:"resources,omitempty"`
}

// AgentBulkOperationUpdate describes an action to be performed on an Environment or Resource during an agent update.
type AgentBulkOperationUpdate struct {
	Add    []string `json:"add,omitempty"`
	Remove []string `json:"remove,omitempty"`
}

// BuildDetails describes the builds being performed on this agent.
type BuildDetails struct {
	Links    *BuildDetailsLinks `json:"_links"`
	Pipeline string             `json:"pipeline"`
	Stage    string             `json:"stage"`
	Job      string             `json:"job"`
}

// BuildDetailsLinks describes the HAL structure for _link objects for the build details.
//go:generate gocd-response-links-generator -type=BuildDetailsLinks
type BuildDetailsLinks struct {
	Job      *url.URL `json:"job"`
	Stage    *url.URL `json:"stage"`
	Pipeline *url.URL `json:"pipeline"`
}

// RemoveLinks sets the `Link` attribute as `nil`. Used when rendering an `Agent` struct to JSON.
func (a *Agent) RemoveLinks() {
	a.Links = nil
}

// List will retrieve all agents, their status, and metadata from the GoCD Server.
func (s *AgentsService) List(ctx context.Context) ([]*Agent, *APIResponse, error) {
	req, err := s.client.NewRequest("GET", "agents", nil, apiV4)
	if err != nil {
		return nil, nil, err
	}

	r := AgentsResponse{}
	resp, err := s.client.Do(ctx, req, &r, responseTypeJSON)
	if err != nil {
		return nil, resp, err
	}

	for _, agent := range r.Embedded.Agents {
		agent.client = s.client
	}

	return r.Embedded.Agents, resp, nil
}

// Get will retrieve a single agent based on the provided UUID.
func (s *AgentsService) Get(ctx context.Context, uuid string) (*Agent, *APIResponse, error) {
	return s.handleAgentRequest(ctx, "GET", uuid, nil)
}

// Update will modify the configuration for an existing agents.
func (s *AgentsService) Update(ctx context.Context, uuid string, agent AgentUpdate) (*Agent, *APIResponse, error) {
	return s.handleAgentRequest(ctx, "PATCH", uuid, &agent)
}

// Delete will remove an existing agent. Note: The agent must be disabled, and not currently building to be deleted.
func (s *AgentsService) Delete(ctx context.Context, uuid string) (string, *APIResponse, error) {
	req, err := s.client.NewRequest("DELETE", "agents/"+uuid, nil, apiV4)
	if err != nil {
		return "", nil, err
	}

	a := StringResponse{}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return "", resp, err
	}

	return a.Message, resp, nil
}

// BulkUpdate will change the configuration for multiple agents in a single request.
func (s *AgentsService) BulkUpdate(ctx context.Context, agents AgentBulkUpdate) (string, *APIResponse, error) {
	req, err := s.client.NewRequest("PATCH", "agents", agents, apiV4)
	if err != nil {
		return "", nil, err
	}

	a := StringResponse{}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return "", resp, err
	}

	return a.Message, resp, nil
}

// JobRunHistory will return a list of Jobs run on this agent.
func (s *AgentsService) JobRunHistory(ctx context.Context, uuid string) ([]*Job, *APIResponse, error) {
	req, err := s.client.NewRequest("GET", fmt.Sprintf("agents/%s/job_run_history", uuid), nil, apiV4)
	if err != nil {
		return nil, nil, err
	}
	a := JobRunHistoryResponse{}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return nil, resp, err
	}

	return a.Jobs, resp, nil

}

// handleAgentRequest handles the flow to perform an HTTP action on an agent resource.
func (s *AgentsService) handleAgentRequest(ctx context.Context, action string, uuid string, body *AgentUpdate) (*Agent, *APIResponse, error) {

	req, err := s.client.NewRequest(action, "agents/"+uuid, body, apiV4)
	if err != nil {
		return nil, nil, err
	}

	a := Agent{client: s.client}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return nil, resp, err
	}

	return &a, resp, nil
}
<commit_msg>Simplified code<commit_after>package gocd

import (
	"context"
	"fmt"
	"net/url"
)

// AgentsService describes the HAL _link resource for the api response object for an agent objects.
type AgentsService service

// AgentsLinks describes the HAL _link resource for the api response object for a collection of agent objects.
//go:generate gocd-response-links-generator -type=AgentsLinks,AgentLinks
type AgentsLinks struct {
	Self *url.URL `json:"self"`
	Doc  *url.URL `json:"doc"`
}

// AgentLinks describes the HAL _link resource for the api response object for a single agent object.
type AgentLinks struct {
	Self *url.URL `json:"self"`
	Doc  *url.URL `json:"doc"`
	Find *url.URL `json:"find"`
}

// AgentsResponse describes the structure of the API response when listing collections of agent object.
type AgentsResponse struct {
	Links *AgentsLinks `json:"_links,omitempty"`
	Embedded *struct {
		Agents []*Agent `json:"agents"`
	} `json:"_embedded,omitempty"`
}

// Agent describes a single agent object.
type Agent struct {
	UUID             string        `json:"uuid"`
	Hostname         string        `json:"hostname"`
	ElasticAgentID   string        `json:"elastic_agent_id"`
	ElasticPluginID  string        `json:"elastic_plugin_id"`
	IPAddress        string        `json:"ip_address"`
	Sandbox          string        `json:"sandbox"`
	OperatingSystem  string        `json:"operating_system"`
	FreeSpace        int           `json:"free_space"`
	AgentConfigState string        `json:"agent_config_state"`
	AgentState       string        `json:"agent_state"`
	Resources        []string      `json:"resources"`
	Environments     []string      `json:"environments"`
	BuildState       string        `json:"build_state"`
	BuildDetails     *BuildDetails `json:"build_details"`
	Links            *AgentLinks   `json:"_links,omitempty"`
	client           *Client
}

// JobRunHistory retrieves the list of jobs run on this agent
func (a *Agent) JobRunHistory(ctx context.Context) ([]*Job, error) {
	jobs, _, err := a.client.Agents.JobRunHistory(ctx, a.UUID)
	if err != nil {
		return nil, err
	}

	return jobs, nil
}

// AgentUpdate describes the structure for the PUT payload when updating an agent
type AgentUpdate struct {
	Hostname         string   `json:"hostname,omitempty"`
	Resources        []string `json:"resources,omitempty"`
	Environments     []string `json:"environments,omitempty"`
	AgentConfigState string   `json:"agent_config_state,omitempty"`
}

// AgentBulkUpdate describes the structure for the PUT payload when updating multiple agents
type AgentBulkUpdate struct {
	Uuids            []string                   `json:"uuids"`
	Operations       *AgentBulkOperationsUpdate `json:"operations,omitempty"`
	AgentConfigState string                     `json:"agent_config_state,omitempty"`
}

// AgentBulkOperationsUpdate describes the structure for a single Operation in AgentBulkUpdate the PUT payload when
// updating multiple agents
type AgentBulkOperationsUpdate struct {
	Environments *AgentBulkOperationUpdate `json:"environments,omitempty"`
	Resources    *AgentBulkOperationUpdate `json:"resources,omitempty"`
}

// AgentBulkOperationUpdate describes an action to be performed on an Environment or Resource during an agent update.
type AgentBulkOperationUpdate struct {
	Add    []string `json:"add,omitempty"`
	Remove []string `json:"remove,omitempty"`
}

// BuildDetails describes the builds being performed on this agent.
type BuildDetails struct {
	Links    *BuildDetailsLinks `json:"_links"`
	Pipeline string             `json:"pipeline"`
	Stage    string             `json:"stage"`
	Job      string             `json:"job"`
}

// BuildDetailsLinks describes the HAL structure for _link objects for the build details.
//go:generate gocd-response-links-generator -type=BuildDetailsLinks
type BuildDetailsLinks struct {
	Job      *url.URL `json:"job"`
	Stage    *url.URL `json:"stage"`
	Pipeline *url.URL `json:"pipeline"`
}

// RemoveLinks sets the `Link` attribute as `nil`. Used when rendering an `Agent` struct to JSON.
func (a *Agent) RemoveLinks() {
	a.Links = nil
}

// List will retrieve all agents, their status, and metadata from the GoCD Server.
func (s *AgentsService) List(ctx context.Context) ([]*Agent, *APIResponse, error) {
	r := AgentsResponse{}
	_, resp, err := s.client.getAction(ctx, &APIClientRequest{
		Path:         "agents",
		ResponseBody: &r,
		APIVersion:   apiV4,
	})

	for _, agent := range r.Embedded.Agents {
		agent.client = s.client
	}

	return r.Embedded.Agents, resp, err
}

// Get will retrieve a single agent based on the provided UUID.
func (s *AgentsService) Get(ctx context.Context, uuid string) (*Agent, *APIResponse, error) {
	return s.handleAgentRequest(ctx, "GET", uuid, nil)
}

// Update will modify the configuration for an existing agents.
func (s *AgentsService) Update(ctx context.Context, uuid string, agent AgentUpdate) (*Agent, *APIResponse, error) {
	return s.handleAgentRequest(ctx, "PATCH", uuid, &agent)
}

// Delete will remove an existing agent. Note: The agent must be disabled, and not currently building to be deleted.
func (s *AgentsService) Delete(ctx context.Context, uuid string) (string, *APIResponse, error) {
	req, err := s.client.NewRequest("DELETE", "agents/"+uuid, nil, apiV4)
	if err != nil {
		return "", nil, err
	}

	a := StringResponse{}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return "", resp, err
	}

	return a.Message, resp, nil
}

// BulkUpdate will change the configuration for multiple agents in a single request.
func (s *AgentsService) BulkUpdate(ctx context.Context, agents AgentBulkUpdate) (string, *APIResponse, error) {
	req, err := s.client.NewRequest("PATCH", "agents", agents, apiV4)
	if err != nil {
		return "", nil, err
	}

	a := StringResponse{}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return "", resp, err
	}

	return a.Message, resp, nil
}

// JobRunHistory will return a list of Jobs run on this agent.
func (s *AgentsService) JobRunHistory(ctx context.Context, uuid string) ([]*Job, *APIResponse, error) {
	req, err := s.client.NewRequest("GET", fmt.Sprintf("agents/%s/job_run_history", uuid), nil, apiV4)
	if err != nil {
		return nil, nil, err
	}
	a := JobRunHistoryResponse{}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return nil, resp, err
	}

	return a.Jobs, resp, nil

}

// handleAgentRequest handles the flow to perform an HTTP action on an agent resource.
func (s *AgentsService) handleAgentRequest(ctx context.Context, action string, uuid string, body *AgentUpdate) (*Agent, *APIResponse, error) {

	req, err := s.client.NewRequest(action, "agents/"+uuid, body, apiV4)
	if err != nil {
		return nil, nil, err
	}

	a := Agent{client: s.client}
	resp, err := s.client.Do(ctx, req, &a, responseTypeJSON)
	if err != nil {
		return nil, resp, err
	}

	return &a, resp, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package godoc

import (
	"net/http"
	"regexp"
	"sync"
	"text/template"

	"golang.org/x/tools/godoc/vfs/httpfs"
)

// SearchResultFunc functions return an HTML body for displaying search results.
type SearchResultFunc func(p *Presentation, result SearchResult) []byte

// Presentation generates output from a corpus.
type Presentation struct {
	Corpus *Corpus

	mux        *http.ServeMux
	fileServer http.Handler
	cmdHandler handlerServer
	pkgHandler handlerServer

	CallGraphHTML,
	DirlistHTML,
	ErrorHTML,
	ExampleHTML,
	GodocHTML,
	ImplementsHTML,
	MethodSetHTML,
	PackageHTML,
	PackageText,
	SearchHTML,
	SearchDocHTML,
	SearchCodeHTML,
	SearchTxtHTML,
	SearchText,
	SearchDescXML *template.Template

	// TabWidth optionally specifies the tab width.
	TabWidth int

	ShowTimestamps bool
	ShowPlayground bool
	ShowExamples   bool
	DeclLinks      bool

	// SrcMode outputs source code instead of documentation in command-line mode.
	SrcMode bool
	// HTMLMode outputs HTML instead of plain text in command-line mode.
	HTMLMode bool

	// NotesRx optionally specifies a regexp to match
	// notes to render in the output.
	NotesRx *regexp.Regexp

	// AdjustPageInfoMode optionally specifies a function to
	// modify the PageInfoMode of a request. The default chosen
	// value is provided.
	AdjustPageInfoMode func(req *http.Request, mode PageInfoMode) PageInfoMode

	// URLForSrc optionally specifies a function that takes a source file and
	// returns a URL for it.
	// The source file argument has the form /src/<path>/<filename>.
	URLForSrc func(src string) string

	// URLForSrcPos optionally specifies a function to create a URL given a
	// source file, a line from the source file (1-based), and low & high offset
	// positions (0-based, bytes from beginning of file). Ideally, the returned
	// URL will be for the specified line of the file, while the high & low
	// positions will be used to highlight a section of the file.
	// The source file argument has the form /src/<path>/<filename>.
	URLForSrcPos func(src string, line, low, high int) string

	// URLForSrcQuery optionally specifies a function to create a URL given a
	// source file, a query string, and a line from the source file (1-based).
	// The source file argument has the form /src/<path>/<filename>.
	// The query argument will be escaped for the purposes of embedding in a URL
	// query parameter.
	// Ideally, the returned URL will be for the specified line of the file with
	// the query string highlighted.
	URLForSrcQuery func(src, query string, line int) string

	// SearchResults optionally specifies a list of functions returning an HTML
	// body for displaying search results.
	SearchResults []SearchResultFunc

	initFuncMapOnce sync.Once
	funcMap         template.FuncMap
	templateFuncs   template.FuncMap
}

// NewPresentation returns a new Presentation from a corpus.
// It sets SearchResults to:
// [SearchResultDoc SearchResultCode SearchResultTxt].
func NewPresentation(c *Corpus) *Presentation {
	if c == nil {
		panic("nil Corpus")
	}
	p := &Presentation{
		Corpus:     c,
		mux:        http.NewServeMux(),
		fileServer: http.FileServer(httpfs.New(c.fs)),

		TabWidth:     4,
		ShowExamples: true,
		DeclLinks:    true,
		SearchResults: []SearchResultFunc{
			(*Presentation).SearchResultDoc,
			(*Presentation).SearchResultCode,
			(*Presentation).SearchResultTxt,
		},
	}
	p.cmdHandler = handlerServer{
		p:           p,
		c:           c,
		pattern:     "/cmd/",
		stripPrefix: "cmd/",
		fsRoot:      "/src/cmd",
	}
	p.pkgHandler = handlerServer{
		p:           p,
		c:           c,
		pattern:     "/pkg/",
		stripPrefix: "pkg/",
		fsRoot:      "/src",
		exclude:     []string{"/src/cmd"},
	}
	p.cmdHandler.registerWithMux(p.mux)
	p.pkgHandler.registerWithMux(p.mux)
	p.mux.HandleFunc("/", p.ServeFile)
	p.mux.HandleFunc("/search", p.HandleSearch)
	p.mux.HandleFunc("/opensearch.xml", p.serveSearchDesc)
	return p
}

func (p *Presentation) FileServer() http.Handler {
	return p.fileServer
}

func (p *Presentation) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.mux.ServeHTTP(w, r)
}

func (p *Presentation) PkgFSRoot() string {
	return p.pkgHandler.fsRoot
}

func (p *Presentation) CmdFSRoot() string {
	return p.cmdHandler.fsRoot
}

// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetPkgPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
	return p.pkgHandler.GetPageInfo(abspath, relpath, mode, "", "")
}

// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetCmdPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
	return p.cmdHandler.GetPageInfo(abspath, relpath, mode, "", "")
}
<commit_msg>Revert "godoc: correct fsRoot to prevent misleading message about cmd/"<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package godoc

import (
	"net/http"
	"regexp"
	"sync"
	"text/template"

	"golang.org/x/tools/godoc/vfs/httpfs"
)

// SearchResultFunc functions return an HTML body for displaying search results.
type SearchResultFunc func(p *Presentation, result SearchResult) []byte

// Presentation generates output from a corpus.
type Presentation struct {
	Corpus *Corpus

	mux        *http.ServeMux
	fileServer http.Handler
	cmdHandler handlerServer
	pkgHandler handlerServer

	CallGraphHTML,
	DirlistHTML,
	ErrorHTML,
	ExampleHTML,
	GodocHTML,
	ImplementsHTML,
	MethodSetHTML,
	PackageHTML,
	PackageText,
	SearchHTML,
	SearchDocHTML,
	SearchCodeHTML,
	SearchTxtHTML,
	SearchText,
	SearchDescXML *template.Template

	// TabWidth optionally specifies the tab width.
	TabWidth int

	ShowTimestamps bool
	ShowPlayground bool
	ShowExamples   bool
	DeclLinks      bool

	// SrcMode outputs source code instead of documentation in command-line mode.
	SrcMode bool
	// HTMLMode outputs HTML instead of plain text in command-line mode.
	HTMLMode bool

	// NotesRx optionally specifies a regexp to match
	// notes to render in the output.
	NotesRx *regexp.Regexp

	// AdjustPageInfoMode optionally specifies a function to
	// modify the PageInfoMode of a request. The default chosen
	// value is provided.
	AdjustPageInfoMode func(req *http.Request, mode PageInfoMode) PageInfoMode

	// URLForSrc optionally specifies a function that takes a source file and
	// returns a URL for it.
	// The source file argument has the form /src/<path>/<filename>.
	URLForSrc func(src string) string

	// URLForSrcPos optionally specifies a function to create a URL given a
	// source file, a line from the source file (1-based), and low & high offset
	// positions (0-based, bytes from beginning of file). Ideally, the returned
	// URL will be for the specified line of the file, while the high & low
	// positions will be used to highlight a section of the file.
	// The source file argument has the form /src/<path>/<filename>.
	URLForSrcPos func(src string, line, low, high int) string

	// URLForSrcQuery optionally specifies a function to create a URL given a
	// source file, a query string, and a line from the source file (1-based).
	// The source file argument has the form /src/<path>/<filename>.
	// The query argument will be escaped for the purposes of embedding in a URL
	// query parameter.
	// Ideally, the returned URL will be for the specified line of the file with
	// the query string highlighted.
	URLForSrcQuery func(src, query string, line int) string

	// SearchResults optionally specifies a list of functions returning an HTML
	// body for displaying search results.
	SearchResults []SearchResultFunc

	initFuncMapOnce sync.Once
	funcMap         template.FuncMap
	templateFuncs   template.FuncMap
}

// NewPresentation returns a new Presentation from a corpus.
// It sets SearchResults to:
// [SearchResultDoc SearchResultCode SearchResultTxt].
func NewPresentation(c *Corpus) *Presentation {
	if c == nil {
		panic("nil Corpus")
	}
	p := &Presentation{
		Corpus:     c,
		mux:        http.NewServeMux(),
		fileServer: http.FileServer(httpfs.New(c.fs)),

		TabWidth:     4,
		ShowExamples: true,
		DeclLinks:    true,
		SearchResults: []SearchResultFunc{
			(*Presentation).SearchResultDoc,
			(*Presentation).SearchResultCode,
			(*Presentation).SearchResultTxt,
		},
	}
	p.cmdHandler = handlerServer{
		p:       p,
		c:       c,
		pattern: "/cmd/",
		fsRoot:  "/src",
	}
	p.pkgHandler = handlerServer{
		p:           p,
		c:           c,
		pattern:     "/pkg/",
		stripPrefix: "pkg/",
		fsRoot:      "/src",
		exclude:     []string{"/src/cmd"},
	}
	p.cmdHandler.registerWithMux(p.mux)
	p.pkgHandler.registerWithMux(p.mux)
	p.mux.HandleFunc("/", p.ServeFile)
	p.mux.HandleFunc("/search", p.HandleSearch)
	p.mux.HandleFunc("/opensearch.xml", p.serveSearchDesc)
	return p
}

func (p *Presentation) FileServer() http.Handler {
	return p.fileServer
}

func (p *Presentation) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.mux.ServeHTTP(w, r)
}

func (p *Presentation) PkgFSRoot() string {
	return p.pkgHandler.fsRoot
}

func (p *Presentation) CmdFSRoot() string {
	return p.cmdHandler.fsRoot
}

// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetPkgPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
	return p.pkgHandler.GetPageInfo(abspath, relpath, mode, "", "")
}

// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetCmdPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
	return p.cmdHandler.GetPageInfo(abspath, relpath, mode, "", "")
}
<|endoftext|>


<commit_before>package gormigrate

import (
	"errors"
	"fmt"

	"github.com/jinzhu/gorm"
)

// MigrateFunc is the func signature for migrating.
type MigrateFunc func(*gorm.DB) error

// RollbackFunc is the func signature for rollbacking.
type RollbackFunc func(*gorm.DB) error

// InitSchemaFunc is the func signature for initializing the schema.
type InitSchemaFunc func(*gorm.DB) error

// Options define options for all migrations.
type Options struct {
	// TableName is the migration table.
	TableName string
	// IDColumnName is the name of column where the migration id will be stored.
	IDColumnName string
	// UseTransaction makes Gormigrate execute migrations inside a single transaction.
	// Keep in mind that not all databases support DDL commands inside transactions.
	UseTransaction bool
}

// Migration represents a database migration (a modification to be made on the database).
type Migration struct {
	// ID is the migration identifier. Usually a timestamp like "201601021504".
	ID string
	// Migrate is a function that will br executed while running this migration.
	Migrate MigrateFunc
	// Rollback will be executed on rollback. Can be nil.
	Rollback RollbackFunc
}

// Gormigrate represents a collection of all migrations of a database schema.
type Gormigrate struct {
	db         *gorm.DB
	tx         *gorm.DB
	options    *Options
	migrations []*Migration
	initSchema InitSchemaFunc
}

// DuplicatedIDError is returned when more than one migration have the same ID
type DuplicatedIDError struct {
	ID string
}

func (e *DuplicatedIDError) Error() string {
	return fmt.Sprintf(`Duplicated migration ID: "%s"`, e.ID)
}

var (
	// DefaultOptions can be used if you don't want to think about options.
	DefaultOptions = &Options{
		TableName:      "migrations",
		IDColumnName:   "id",
		UseTransaction: false,
	}

	// ErrRollbackImpossible is returned when trying to rollback a migration
	// that has no rollback function.
	ErrRollbackImpossible = errors.New("It's impossible to rollback this migration")

	// ErrNoMigrationDefined is returned when no migration is defined.
	ErrNoMigrationDefined = errors.New("No migration defined")

	// ErrMissingID is returned when the ID od migration is equal to ""
	ErrMissingID = errors.New("Missing ID in migration")

	// ErrNoRunnedMigration is returned when any runned migration was found while
	// running RollbackLast
	ErrNoRunnedMigration = errors.New("Could not find last runned migration")
)

// New returns a new Gormigrate.
func New(db *gorm.DB, options *Options, migrations []*Migration) *Gormigrate {
	if options.TableName == "" {
		options.TableName = DefaultOptions.TableName
	}
	if options.IDColumnName == "" {
		options.IDColumnName = DefaultOptions.IDColumnName
	}
	return &Gormigrate{
		db:         db,
		options:    options,
		migrations: migrations,
	}
}

// InitSchema sets a function that is run if no migration is found.
// The idea is preventing to run all migrations when a new clean database
// is being migrating. In this function you should create all tables and
// foreign key necessary to your application.
func (g *Gormigrate) InitSchema(initSchema InitSchemaFunc) {
	g.initSchema = initSchema
}

// Migrate executes all migrations that did not run yet.
func (g *Gormigrate) Migrate() error {
	if err := g.checkDuplicatedID(); err != nil {
		return err
	}

	if err := g.createMigrationTableIfNotExists(); err != nil {
		return err
	}

	g.begin()

	if g.initSchema != nil && g.isFirstRun() {
		if err := g.runInitSchema(); err != nil {
			g.rollback()
			return err
		}
		return g.commit()
	}

	for _, migration := range g.migrations {
		if err := g.runMigration(migration); err != nil {
			g.rollback()
			return err
		}
	}

	return g.commit()
}

func (g *Gormigrate) checkDuplicatedID() error {
	lookup := make(map[string]struct{}, len(g.migrations))
	for _, m := range g.migrations {
		if _, ok := lookup[m.ID]; ok {
			return &DuplicatedIDError{ID: m.ID}
		}
		lookup[m.ID] = struct{}{}
	}
	return nil
}

// RollbackLast undo the last migration
func (g *Gormigrate) RollbackLast() error {
	if len(g.migrations) == 0 {
		return ErrNoMigrationDefined
	}

	lastRunnedMigration, err := g.getLastRunnedMigration()
	if err != nil {
		return err
	}

	if err := g.RollbackMigration(lastRunnedMigration); err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) getLastRunnedMigration() (*Migration, error) {
	for i := len(g.migrations) - 1; i >= 0; i-- {
		migration := g.migrations[i]
		if g.migrationDidRun(migration) {
			return migration, nil
		}
	}
	return nil, ErrNoRunnedMigration
}

// RollbackMigration undo a migration.
func (g *Gormigrate) RollbackMigration(m *Migration) error {
	if m.Rollback == nil {
		return ErrRollbackImpossible
	}

	g.begin()

	if err := m.Rollback(g.tx); err != nil {
		return err
	}

	sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", g.options.TableName, g.options.IDColumnName)
	if err := g.db.Exec(sql, m.ID).Error; err != nil {
		g.rollback()
		return err
	}

	return g.commit()
}

func (g *Gormigrate) runInitSchema() error {
	if err := g.initSchema(g.tx); err != nil {
		return err
	}

	for _, migration := range g.migrations {
		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}

	return nil
}

func (g *Gormigrate) runMigration(migration *Migration) error {
	if len(migration.ID) == 0 {
		return ErrMissingID
	}

	if !g.migrationDidRun(migration) {
		if err := migration.Migrate(g.tx); err != nil {
			return err
		}

		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) createMigrationTableIfNotExists() error {
	if g.db.HasTable(g.options.TableName) {
		return nil
	}

	sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY)", g.options.TableName, g.options.IDColumnName)
	if err := g.db.Exec(sql).Error; err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) migrationDidRun(m *Migration) bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Where(fmt.Sprintf("%s = ?", g.options.IDColumnName), m.ID).
		Count(&count)
	return count > 0
}

func (g *Gormigrate) isFirstRun() bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Count(&count)
	return count == 0
}

func (g *Gormigrate) insertMigration(id string) error {
	sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", g.options.TableName, g.options.IDColumnName)
	return g.tx.Exec(sql, id).Error
}

func (g *Gormigrate) begin() {
	if g.options.UseTransaction {
		g.tx = g.db.Begin()
	} else {
		g.tx = g.db
	}
}

func (g *Gormigrate) commit() error {
	if g.options.UseTransaction {
		if err := g.tx.Commit().Error; err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) rollback() {
	if g.options.UseTransaction {
		g.tx.Rollback()
	}
}
<commit_msg>add id column size option<commit_after>package gormigrate

import (
	"errors"
	"fmt"

	"github.com/jinzhu/gorm"
)

// MigrateFunc is the func signature for migrating.
type MigrateFunc func(*gorm.DB) error

// RollbackFunc is the func signature for rollbacking.
type RollbackFunc func(*gorm.DB) error

// InitSchemaFunc is the func signature for initializing the schema.
type InitSchemaFunc func(*gorm.DB) error

// Options define options for all migrations.
type Options struct {
	// TableName is the migration table.
	TableName string
	// IDColumnName is the name of column where the migration id will be stored.
	IDColumnName string
	// IDColumnSize is the length of the migration id column
	IDColumnSize int
	// UseTransaction makes Gormigrate execute migrations inside a single transaction.
	// Keep in mind that not all databases support DDL commands inside transactions.
	UseTransaction bool
}

// Migration represents a database migration (a modification to be made on the database).
type Migration struct {
	// ID is the migration identifier. Usually a timestamp like "201601021504".
	ID string
	// Migrate is a function that will br executed while running this migration.
	Migrate MigrateFunc
	// Rollback will be executed on rollback. Can be nil.
	Rollback RollbackFunc
}

// Gormigrate represents a collection of all migrations of a database schema.
type Gormigrate struct {
	db         *gorm.DB
	tx         *gorm.DB
	options    *Options
	migrations []*Migration
	initSchema InitSchemaFunc
}

// DuplicatedIDError is returned when more than one migration have the same ID
type DuplicatedIDError struct {
	ID string
}

func (e *DuplicatedIDError) Error() string {
	return fmt.Sprintf(`Duplicated migration ID: "%s"`, e.ID)
}

var (
	// DefaultOptions can be used if you don't want to think about options.
	DefaultOptions = &Options{
		TableName:      "migrations",
		IDColumnName:   "id",
		IDColumnSize:   255,
		UseTransaction: false,
	}

	// ErrRollbackImpossible is returned when trying to rollback a migration
	// that has no rollback function.
	ErrRollbackImpossible = errors.New("It's impossible to rollback this migration")

	// ErrNoMigrationDefined is returned when no migration is defined.
	ErrNoMigrationDefined = errors.New("No migration defined")

	// ErrMissingID is returned when the ID od migration is equal to ""
	ErrMissingID = errors.New("Missing ID in migration")

	// ErrNoRunnedMigration is returned when any runned migration was found while
	// running RollbackLast
	ErrNoRunnedMigration = errors.New("Could not find last runned migration")
)

// New returns a new Gormigrate.
func New(db *gorm.DB, options *Options, migrations []*Migration) *Gormigrate {
	if options.TableName == "" {
		options.TableName = DefaultOptions.TableName
	}
	if options.IDColumnName == "" {
		options.IDColumnName = DefaultOptions.IDColumnName
	}
	if options.IDColumnSize == 0 {
		options.IDColumnSize = DefaultOptions.IDColumnSize
	}
	return &Gormigrate{
		db:         db,
		options:    options,
		migrations: migrations,
	}
}

// InitSchema sets a function that is run if no migration is found.
// The idea is preventing to run all migrations when a new clean database
// is being migrating. In this function you should create all tables and
// foreign key necessary to your application.
func (g *Gormigrate) InitSchema(initSchema InitSchemaFunc) {
	g.initSchema = initSchema
}

// Migrate executes all migrations that did not run yet.
func (g *Gormigrate) Migrate() error {
	if err := g.checkDuplicatedID(); err != nil {
		return err
	}

	if err := g.createMigrationTableIfNotExists(); err != nil {
		return err
	}

	g.begin()

	if g.initSchema != nil && g.isFirstRun() {
		if err := g.runInitSchema(); err != nil {
			g.rollback()
			return err
		}
		return g.commit()
	}

	for _, migration := range g.migrations {
		if err := g.runMigration(migration); err != nil {
			g.rollback()
			return err
		}
	}

	return g.commit()
}

func (g *Gormigrate) checkDuplicatedID() error {
	lookup := make(map[string]struct{}, len(g.migrations))
	for _, m := range g.migrations {
		if _, ok := lookup[m.ID]; ok {
			return &DuplicatedIDError{ID: m.ID}
		}
		lookup[m.ID] = struct{}{}
	}
	return nil
}

// RollbackLast undo the last migration
func (g *Gormigrate) RollbackLast() error {
	if len(g.migrations) == 0 {
		return ErrNoMigrationDefined
	}

	lastRunnedMigration, err := g.getLastRunnedMigration()
	if err != nil {
		return err
	}

	if err := g.RollbackMigration(lastRunnedMigration); err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) getLastRunnedMigration() (*Migration, error) {
	for i := len(g.migrations) - 1; i >= 0; i-- {
		migration := g.migrations[i]
		if g.migrationDidRun(migration) {
			return migration, nil
		}
	}
	return nil, ErrNoRunnedMigration
}

// RollbackMigration undo a migration.
func (g *Gormigrate) RollbackMigration(m *Migration) error {
	if m.Rollback == nil {
		return ErrRollbackImpossible
	}

	g.begin()

	if err := m.Rollback(g.tx); err != nil {
		return err
	}

	sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", g.options.TableName, g.options.IDColumnName)
	if err := g.db.Exec(sql, m.ID).Error; err != nil {
		g.rollback()
		return err
	}

	return g.commit()
}

func (g *Gormigrate) runInitSchema() error {
	if err := g.initSchema(g.tx); err != nil {
		return err
	}

	for _, migration := range g.migrations {
		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}

	return nil
}

func (g *Gormigrate) runMigration(migration *Migration) error {
	if len(migration.ID) == 0 {
		return ErrMissingID
	}

	if !g.migrationDidRun(migration) {
		if err := migration.Migrate(g.tx); err != nil {
			return err
		}

		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) createMigrationTableIfNotExists() error {
	if g.db.HasTable(g.options.TableName) {
		return nil
	}

	sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(%d) PRIMARY KEY)", g.options.TableName, g.options.IDColumnName, g.options.IDColumnSize)
	if err := g.db.Exec(sql).Error; err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) migrationDidRun(m *Migration) bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Where(fmt.Sprintf("%s = ?", g.options.IDColumnName), m.ID).
		Count(&count)
	return count > 0
}

func (g *Gormigrate) isFirstRun() bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Count(&count)
	return count == 0
}

func (g *Gormigrate) insertMigration(id string) error {
	sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", g.options.TableName, g.options.IDColumnName)
	return g.tx.Exec(sql, id).Error
}

func (g *Gormigrate) begin() {
	if g.options.UseTransaction {
		g.tx = g.db.Begin()
	} else {
		g.tx = g.db
	}
}

func (g *Gormigrate) commit() error {
	if g.options.UseTransaction {
		if err := g.tx.Commit().Error; err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) rollback() {
	if g.options.UseTransaction {
		g.tx.Rollback()
	}
}
<|endoftext|>


<commit_before>// Package gotiscrape provides simple utility functions to carve
// typically used as threat intelligence from a body of text.
package gotiscrape

import (
	"net"
	"strings"
)

// FindAll extracts all known patterns from a body of text and returns them
// in a map[string][]string. If you are only interested in specific body,
// you are adviced to use the function directly.
func FindAll(body string) map[string][]string {
	ret := make(map[string][]string)

	ret["md5"] = FindMd5(body)
	ret["sha1"] = FindSha1(body)
	ret["sha2"] = FindSha2(body)
	ret["ipv4"] = FindIPv4(body)
	ret["ipv6"] = FindIPv6(body)
	ret["email"] = FindEmail(body)
	ret["fqdn"] = FindFQDN(body)

	return ret
}

// FindMd5 extracts all md5 like hexdigest strings from a body of text and
// returns them as a string slice.
func FindMd5(body string) []string {
	return dedup(md5.FindAllString(body, -1))
}

// FindSha1 extracts all sha1 hexdigest like strings from a body of text and
// returns them as a string slice.
func FindSha1(body string) []string {
	return dedup(sha1.FindAllString(body, -1))
}

// FindSha2 extracts all sha256 hexdigest like strings from a body of text and
// returns them as a string slice.
func FindSha2(body string) []string {
	return dedup(sha2.FindAllString(body, -1))
}

// FindIPv4 extracts all IPv4 like strings from a body of text and
// returns them as a string slice.
func FindIPv4(body string) []string {
	return dedup(ipv4.FindAllString(body, -1))
}

// FindIPv6 extracts all IPv6 like strings from a body of text and
// returns them as a string slice.
func FindIPv6(body string) []string {

	// retrieve a string slice of all words (between word boundries)
	// that could possibly be a IPv6 string and validate them using
	// package net
	allpos := dedup(allposipv6.FindAllString(body, -1))

	var ret []string

	for _, posipv6 := range allpos {
		if ip := net.ParseIP(posipv6); ip != nil {
			// since ip is ok and we know that ParseIP will
			// return either an ipv4 OR an ipv6 adress and
			// .To4() doc say "If ip is not an IPv4 address, To4 returns nil"
			// we can assume that a .To4() returning nil will in effect mean
			// .IsIPv6() == true
			if ip.To4() == nil {
				ret = append(ret, posipv6)
			}
		}
	}

	return ret
}

// FindEmail extracts all email like strings from a body of text and
// returns them as a string slice.
func FindEmail(body string) []string {
	return dedup(email.FindAllString(body, -1))
}

// FindFQDN extracts all fqdn like strings from a body of text and returns them
// as a string slice. The finds are filtered against a predefined string slice
// of valid top-level domains (TLD) before return.
func FindFQDN(body string) []string {
	return endingInTLD(dedup(fqdn.FindAllString(body, -1)))
}

// dedup removes duplicates from a string slice of strings
func dedup(s []string) []string {
	t := make(map[string]bool)
	for _, e := range s {
		t[e] = true
	}
	var l []string
	for k := range t {
		l = append(l, k)
	}
	return l
}

// endingInTLD filters a string slice against a string slice of TLDs, only
// returning strings thats ends in a valid TLD
func endingInTLD(s []string) []string {
	t := make(map[string]bool)
	for _, e := range s {
		n := strings.Split(e, ".")
		tl := strings.ToTitle(n[len(n)-1])
		for _, tld := range tld {
			if tld == tl {
				t[e] = true
				break
			}
		}
	}
	var l []string
	for k := range t {
		l = append(l, k)
	}

	return l
}
<commit_msg>removed unecessary dedup in endingInTLD()<commit_after>// Package gotiscrape provides simple utility functions to carve
// typically used as threat intelligence from a body of text.
package gotiscrape

import (
	"net"
	"strings"
)

// FindAll extracts all known patterns from a body of text and returns them
// in a map[string][]string. If you are only interested in specific body,
// you are adviced to use the function directly.
func FindAll(body string) map[string][]string {
	ret := make(map[string][]string)

	ret["md5"] = FindMd5(body)
	ret["sha1"] = FindSha1(body)
	ret["sha2"] = FindSha2(body)
	ret["ipv4"] = FindIPv4(body)
	ret["ipv6"] = FindIPv6(body)
	ret["email"] = FindEmail(body)
	ret["fqdn"] = FindFQDN(body)

	return ret
}

// FindMd5 extracts all md5 like hexdigest strings from a body of text and
// returns them as a string slice.
func FindMd5(body string) []string {
	return dedup(md5.FindAllString(body, -1))
}

// FindSha1 extracts all sha1 hexdigest like strings from a body of text and
// returns them as a string slice.
func FindSha1(body string) []string {
	return dedup(sha1.FindAllString(body, -1))
}

// FindSha2 extracts all sha256 hexdigest like strings from a body of text and
// returns them as a string slice.
func FindSha2(body string) []string {
	return dedup(sha2.FindAllString(body, -1))
}

// FindIPv4 extracts all IPv4 like strings from a body of text and
// returns them as a string slice.
func FindIPv4(body string) []string {
	return dedup(ipv4.FindAllString(body, -1))
}

// FindIPv6 extracts all IPv6 like strings from a body of text and
// returns them as a string slice.
func FindIPv6(body string) []string {

	// retrieve a string slice of all words (between word boundries)
	// that could possibly be a IPv6 string and validate them using
	// package net
	allpos := dedup(allposipv6.FindAllString(body, -1))

	var ret []string

	for _, posipv6 := range allpos {
		if ip := net.ParseIP(posipv6); ip != nil {
			// since ip is ok and we know that ParseIP will
			// return either an ipv4 OR an ipv6 adress and
			// .To4() doc say "If ip is not an IPv4 address, To4 returns nil"
			// we can assume that a .To4() returning nil will in effect mean
			// .IsIPv6() == true
			if ip.To4() == nil {
				ret = append(ret, posipv6)
			}
		}
	}

	return ret
}

// FindEmail extracts all email like strings from a body of text and
// returns them as a string slice.
func FindEmail(body string) []string {
	return dedup(email.FindAllString(body, -1))
}

// FindFQDN extracts all fqdn like strings from a body of text and returns them
// as a string slice. The finds are filtered against a predefined string slice
// of valid top-level domains (TLD) before return.
func FindFQDN(body string) []string {
	return endingInTLD(dedup(fqdn.FindAllString(body, -1)))
}

// dedup removes duplicates from a string slice of strings
func dedup(s []string) []string {
	t := make(map[string]bool)
	for _, e := range s {
		t[e] = true
	}
	var l []string
	for k := range t {
		l = append(l, k)
	}
	return l
}

// endingInTLD filters a string slice against a string slice of TLDs, only
// returning strings thats ends in a valid TLD
func endingInTLD(s []string) []string {
	var l []string
	for _, e := range s {
		n := strings.Split(e, ".")
		tl := strings.ToTitle(n[len(n)-1])
		for _, tld := range tld {
			if tld == tl {
				l = append(l, e)
				break
			}
		}
	}

	return l
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/log"
)

const viewsDir = "views"

var views *template.Template

func init() {
	if _, err := os.Stat(viewsDir); err != nil {
		return
	}

	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(viewsDir, viewsWalk); err != nil {
			panic(fmt.Errorf("response: %v", err))
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func viewsWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(c.ResponseWriter).Encode(v); err != nil {
		log.Stack(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	if views == nil {
		log.Stack(errors.New(`views can't be used without a "views" directory`))
	}
	data["c"] = c
	if err := views.ExecuteTemplate(c.ResponseWriter, name, data); err != nil {
		log.Stack(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<commit_msg>Simplify panic<commit_after>package response

import (
	"encoding/json"
	"errors"
	"html/template"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/log"
)

const viewsDir = "views"

var views *template.Template

func init() {
	if _, err := os.Stat(viewsDir); err != nil {
		return
	}

	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(viewsDir, viewsWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func viewsWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(c.ResponseWriter).Encode(v); err != nil {
		log.Stack(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	if views == nil {
		log.Stack(errors.New(`views can't be used without a "views" directory`))
	}
	data["c"] = c
	if err := views.ExecuteTemplate(c.ResponseWriter, name, data); err != nil {
		log.Stack(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>package gohex

import (
	"encoding/binary"
	"errors"
	"fmt"
)

func checkSum(bytes []byte) error {
	sum := 0
	for _, b := range bytes[:len(bytes)-1] {
		sum += int(b)
	}
	sum %= 256
	sum = 256 - sum
	last := int(bytes[len(bytes)-1])
	if sum != last {
		return errors.New("incorrect checksum (sum = " + fmt.Sprintf("%02X != %02X", sum, last) + ")")
	}
	return nil
}

func checkRecordSize(bytes []byte) error {
	if (int(bytes[0]) + 5) != len(bytes) {
		return errors.New("incorrect data length")
	}
	return nil
}

func checkEOF(bytes []byte) error {
	if bytes[0] != 0 {
		return errors.New("incorrect data length field in eof line")
	}
	if binary.BigEndian.Uint16(bytes[1:3]) != 0 {
		return errors.New("incorrect address field in eof line")
	}
	return nil
}

func getExtendedAddress(bytes []byte) (int, error) {
	if bytes[0] != 2 {
		return 0, errors.New("incorrect data length field in extended linear address line")
	}
	if binary.BigEndian.Uint16(bytes[1:3]) != 0 {
		return 0, errors.New("incorrect address field in extended linear address line")
	}
	adr := int(binary.BigEndian.Uint16(bytes[4:6])) << 16
	return adr, nil
}

func getDataLine(bytes []byte) (int, []byte) {
	size := bytes[0]
	adr := int(binary.BigEndian.Uint16(bytes[1:3]))
	data := bytes[4 : size+4]
	return adr, data
}

func getStartAddress(bytes []byte) (int, error) {
	if bytes[0] != 4 {
		return 0, errors.New("incorrect data length field in start address line")
	}
	if binary.BigEndian.Uint16(bytes[1:3]) != 0 {
		return 0, errors.New("incorrect address field in start address line")
	}
	adr := int(binary.BigEndian.Uint32(bytes[4:8]))
	return adr, nil
}
<commit_msg>separated sum calculate and checking<commit_after>package gohex

import (
	"encoding/binary"
	"errors"
	"fmt"
)

func calcSum(bytes []byte) byte {
	sum := 0
	for _, b := range bytes {
		sum += int(b)
	}
	sum %= 256
	sum = 256 - sum
	return byte(sum)
}

func checkSum(bytes []byte) error {
	sum := calcSum(bytes[:len(bytes)-1])
	last := bytes[len(bytes)-1]
	if sum != last {
		return errors.New("incorrect checksum (sum = " + fmt.Sprintf("%02X != %02X", sum, last) + ")")
	}
	return nil
}

func checkRecordSize(bytes []byte) error {
	if (int(bytes[0]) + 5) != len(bytes) {
		return errors.New("incorrect data length")
	}
	return nil
}

func checkEOF(bytes []byte) error {
	if bytes[0] != 0 {
		return errors.New("incorrect data length field in eof line")
	}
	if binary.BigEndian.Uint16(bytes[1:3]) != 0 {
		return errors.New("incorrect address field in eof line")
	}
	return nil
}

func getExtendedAddress(bytes []byte) (int, error) {
	if bytes[0] != 2 {
		return 0, errors.New("incorrect data length field in extended linear address line")
	}
	if binary.BigEndian.Uint16(bytes[1:3]) != 0 {
		return 0, errors.New("incorrect address field in extended linear address line")
	}
	adr := int(binary.BigEndian.Uint16(bytes[4:6])) << 16
	return adr, nil
}

func getDataLine(bytes []byte) (int, []byte) {
	size := bytes[0]
	adr := int(binary.BigEndian.Uint16(bytes[1:3]))
	data := bytes[4 : size+4]
	return adr, data
}

func getStartAddress(bytes []byte) (int, error) {
	if bytes[0] != 4 {
		return 0, errors.New("incorrect data length field in start address line")
	}
	if binary.BigEndian.Uint16(bytes[1:3]) != 0 {
		return 0, errors.New("incorrect address field in start address line")
	}
	adr := int(binary.BigEndian.Uint32(bytes[4:8]))
	return adr, nil
}

func makeLine(size int, adr int, recordType byte, data []byte) {
	//fmt.Sprintf(":04000005, a)
}
<|endoftext|>


<commit_before>// http://cryptopals.com/sets/1/
package cryptopals

import (
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"testing"
	"unicode"
)

// Challenge 1 converts a provided hex string to binary and then from binary to base64, verifying its result
// http://cryptopals.com/sets/1/challenges/1/
func TestSet1Challenge1(test *testing.T) {
	input := "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"
	output := "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"
	bytes := hexToBytes(input)
	result := bytesToBase64(bytes)

	if result != output {
		test.Errorf("base64 encoded string was %v but %v was expected", result, input)
	}

}

// Challenge 2 takes tow inputs and xor's them together
// http://cryptopals.com/sets/1/challenges/2/
func TestSet1Challenge2(test *testing.T) {
	input1 := "1c0111001f010100061a024b53535009181c"
	input2 := "686974207468652062756c6c277320657965"
	expected := "746865206b696420646f6e277420706c6179"
	b1 := hexToBytes(input1)
	b2 := hexToBytes(input2)
	result := bytesToHex(xor(b1, b2))
	if result != expected {
		test.Errorf("expected %v but got %v", expected, result)
	}
}

func TestSet1Challenge3(test *testing.T) {
	hexInput := "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"
	output := decodeSingleCharXor(hexToBytes(hexInput))
	expected := "Cooking MC's like a pound of bacon"
	if output.text != expected {
		test.Errorf("expected the input to decode to %v but got %v instead", expected, output.text)
	}

	if output.key != 'X' {
		test.Errorf("expected the key to be guessed as 'A' but was %q", output.key)
	}
}

type solution struct {
	score float64
	text  string
	key   rune
}

func decodeSingleCharXor(bytes []byte) *solution {

	bestSolution := new(solution)
	for key := 0; key < 256; key++ {
		xord := make([]byte, len(bytes))
		for i := range bytes {
			xord[i] = bytes[i] ^ byte(key)
		}

		decrypted := string(xord)
		score := score(decrypted)
		if score > bestSolution.score {
			bestSolution = &solution{score, decrypted, rune(key)}
		}
		fmt.Printf("%v: %q\n", score, decrypted)
	}
	return bestSolution
}

// frequency returns a value indicating a given rune's frequency
// in the english language, values are only guarunteed to be useful in
// comparison to each other and not as any absolute metric
var runeFreq = map[rune]float64{
	' ': 24.50,
	'E': 12.51,
	'T': 9.25,
	'A': 8.04,
	'O': 7.60,
	'I': 7.26,
	'N': 7.09,
	'S': 6.54,
	'R': 6.12,
	'H': 5.49,
	'L': 4.14,
	'D': 3.99,
	'C': 3.06,
	'U': 2.71,
	'M': 2.53,
	'F': 2.30,
	'P': 2.00,
	'G': 1.96,
	'W': 1.92,
	'Y': 1.73,
	'B': 1.54,
	'V': 0.99,
	'K': 0.67,
	'X': 0.19,
	'J': 0.16,
	'Q': 0.11,
	'Z': 0.09,
}

func score(s string) float64 {
	score := 0.0
	for _, r := range s {
		// penalize heavily for non printable characters, since they are pretty good indicators
		// that the bytes being examined are not english/characters
		if !unicode.IsPrint(r) {
			score -= 120
		} else {
			score += runeFreq[unicode.ToUpper(r)]
		}
	}
	return score / float64(len(s))
}

func hexToBytes(s string) []byte {
	bytes, e := hex.DecodeString(s)
	if e != nil {
		panic(e)
	}
	return bytes
}

func base64ToBytes(s string) []byte {
	b, e := base64.StdEncoding.DecodeString(s)
	if e != nil {
		panic(e)
	}
	return b
}

func bytesToHex(b []byte) string {
	return hex.EncodeToString(b)
}

func bytesToBase64(b []byte) string {
	return base64.StdEncoding.EncodeToString(b)
}

func xor(a []byte, b []byte) []byte {
	if len(a) != len(b) {
		panic(fmt.Sprintf("array a and b had differing lengths (%v and %v respectievely", len(a), len(b)))
	}

	l := len(a)
	out := make([]byte, l)
	for i := 0; i < len(a); i++ {
		out[i] = a[i] ^ b[i]
	}
	return out
}
<commit_msg>Really rough pass at challenge 4 of set 1<commit_after>// http://cryptopals.com/sets/1/
package cryptopals

import (
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
	"unicode"
)

// Challenge 1 converts a provided hex string to binary and then from binary to base64, verifying its result
// http://cryptopals.com/sets/1/challenges/1/
func TestSet1Challenge1(test *testing.T) {
	input := "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"
	output := "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"
	bytes := hexToBytes(input)
	result := bytesToBase64(bytes)

	if result != output {
		test.Errorf("base64 encoded string was %v but %v was expected", result, input)
	}

}

// Challenge 2 takes tow inputs and xor's them together
// http://cryptopals.com/sets/1/challenges/2/
func TestSet1Challenge2(test *testing.T) {
	input1 := "1c0111001f010100061a024b53535009181c"
	input2 := "686974207468652062756c6c277320657965"
	expected := "746865206b696420646f6e277420706c6179"
	b1 := hexToBytes(input1)
	b2 := hexToBytes(input2)
	result := bytesToHex(xor(b1, b2))
	if result != expected {
		test.Errorf("expected %v but got %v", expected, result)
	}
}

// Challenge 3 is to crack a hex string that has been xor'd with a 1 byte key
// http://cryptopals.com/sets/1/challenges/3/
func TestSet1Challenge3(test *testing.T) {
	hexInput := "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"
	output := decodeSingleCharXor(hexToBytes(hexInput))
	expected := "Cooking MC's like a pound of bacon"
	if output.text != expected {
		test.Errorf("expected the input to decode to %v but got %v instead", expected, output.text)
	}

	if output.key != 'X' {
		test.Errorf("expected the key to be guessed as 'A' but was %q", output.key)
	}
}

// Challenge 4 is to locate the 1 string within 60 hex snippets that is a
// an english string xor'd with a 1 byte key
// http://cryptopals.com/sets/1/challenges/4/
func TestSet1Challenge4(test *testing.T) {
	response, e := http.Get("http://cryptopals.com/static/challenge-data/4.txt")
	if e != nil {
		panic(e)
	}
	defer response.Body.Close()
	codes, e := ioutil.ReadAll(response.Body)
	if e != nil {
		panic(e)
	}

	expected := "Now that the party is jumping\n"
	var found string
	for _, code := range strings.Split(string(codes), "\n") {
		s := decodeSingleCharXor(hexToBytes(code))
		if s.score > 4.6 {
			found = s.text
		}
	}

	if found != expected {
		test.Errorf("expected clear text to be found with message %v but found %v", expected, found)
	}
}

type solution struct {
	score float64
	text  string
	key   rune
}

func decodeSingleCharXor(bytes []byte) *solution {

	bestSolution := new(solution)
	for key := 0; key < 256; key++ {
		xord := make([]byte, len(bytes))
		for i := range bytes {
			xord[i] = bytes[i] ^ byte(key)
		}

		decrypted := string(xord)
		score := score(decrypted)
		if score > bestSolution.score {
			bestSolution = &solution{score, decrypted, rune(key)}
		}
	}
	return bestSolution
}

// frequency returns a value indicating a given rune's frequency
// in the english language, values are only guarunteed to be useful in
// comparison to each other and not as any absolute metric
var runeFreq = map[rune]float64{
	' ': 24.50,
	'E': 12.51,
	'T': 9.25,
	'A': 8.04,
	'O': 7.60,
	'I': 7.26,
	'N': 7.09,
	'S': 6.54,
	'R': 6.12,
	'H': 5.49,
	'L': 4.14,
	'D': 3.99,
	'C': 3.06,
	'U': 2.71,
	'M': 2.53,
	'F': 2.30,
	'P': 2.00,
	'G': 1.96,
	'W': 1.92,
	'Y': 1.73,
	'B': 1.54,
	'V': 0.99,
	'K': 0.67,
	'X': 0.19,
	'J': 0.16,
	'Q': 0.11,
	'Z': 0.09,
}

func score(s string) float64 {
	score := 0.0
	for _, r := range s {
		// penalize heavily for non printable characters, since they are pretty good indicators
		// that the bytes being examined are not english/characters
		if r > 255 {
			score -= 240
		} else {
			score += runeFreq[unicode.ToUpper(r)]
		}
	}
	return score / float64(len(s))
}

func hexToBytes(s string) []byte {
	bytes, e := hex.DecodeString(s)
	if e != nil {
		panic(e)
	}
	return bytes
}

func base64ToBytes(s string) []byte {
	b, e := base64.StdEncoding.DecodeString(s)
	if e != nil {
		panic(e)
	}
	return b
}

func bytesToHex(b []byte) string {
	return hex.EncodeToString(b)
}

func bytesToBase64(b []byte) string {
	return base64.StdEncoding.EncodeToString(b)
}

func xor(a []byte, b []byte) []byte {
	if len(a) != len(b) {
		panic(fmt.Sprintf("array a and b had differing lengths (%v and %v respectievely", len(a), len(b)))
	}

	l := len(a)
	out := make([]byte, l)
	for i := 0; i < len(a); i++ {
		out[i] = a[i] ^ b[i]
	}
	return out
}
<|endoftext|>