diff --git a/appview/db/pulls.go b/appview/db/pulls.go
--- a/appview/db/pulls.go
+++ b/appview/db/pulls.go
@@ -83,16 +83,10 @@ err := e.QueryRow(`select pull_at from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&pullAt)
return pullAt, err
}
-func GetPullId(e Execer, repoAt syntax.ATURI) (int, error) {
+func NextPullId(e Execer, repoAt syntax.ATURI) (int, error) {
var pullId int
err := e.QueryRow(`select next_pull_id from repo_pull_seqs where repo_at = ?`, repoAt).Scan(&pullId)
return pullId - 1, err
-}
-
-func GetPullOwnerDid(e Execer, repoAt syntax.ATURI, pullId int) (string, error) {
- var ownerDid string
- err := e.QueryRow(`select owner_did from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&ownerDid)
- return ownerDid, err
}
func GetPulls(e Execer, repoAt syntax.ATURI) ([]Pull, error) {
diff --git a/appview/pages/pages.go b/appview/pages/pages.go
--- a/appview/pages/pages.go
+++ b/appview/pages/pages.go
@@ -509,6 +509,7 @@
type RepoNewPullParams struct {
LoggedInUser *auth.User
RepoInfo RepoInfo
+ Branches []types.Branch
Active string
}
diff --git a/appview/pages/templates/repo/pulls/new.html b/appview/pages/templates/repo/pulls/new.html
--- a/appview/pages/templates/repo/pulls/new.html
+++ b/appview/pages/templates/repo/pulls/new.html
@@ -15,7 +15,16 @@
The branch you want to make your change against.
-
diff --git a/appview/randomart/randomart.go b/appview/randomart/randomart.go
new file mode 100644
--- /dev/null
+++ b/appview/randomart/randomart.go
@@ -0,0 +1,173 @@
+package randomart
+
+/*
+ * Draw an ASCII-Art representing the fingerprint so human brain can
+ * profit from its built-in pattern recognition ability.
+ * This technique is called "random art" and can be found in some
+ * scientific publications like this original paper:
+ *
+ * "Hash Visualization: a New Technique to improve Real-World Security",
+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ * Techniques and E-Commerce (CrypTEC '99)
+ * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+ *
+ * The subject came up in a talk by Dan Kaminsky, too.
+ *
+ * If you see the picture is different, the key is different.
+ * If the picture looks the same, you still know nothing.
+ *
+ * The algorithm used here is a worm crawling over a discrete plane,
+ * leaving a trace (augmenting the field) everywhere it goes.
+ * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
+ * makes the respective movement vector be ignored for this turn.
+ * Graphs are not unambiguous, because circles in graphs can be
+ * walked in either direction.
+ */
+
+import (
+ "os"
+)
+
+func MAX(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func MIN(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+/*
+ * Field sizes for the random art. Have to be odd, so the starting point
+ * can be in the exact middle of the picture, and FLDBASE should be >=8 .
+ * Else pictures would be too dense, and drawing the frame would
+ * fail, too, because the key type would not fit in anymore.
+ */
+const (
+ FLDBASE = 8
+ FLDSIZE_Y = (FLDBASE + 1)
+ FLDSIZE_X = (FLDBASE*2 + 1)
+)
+
+func FromString(str string) string {
+ ch := make(chan byte)
+
+ go func() {
+ defer close(ch)
+ for _, v := range []byte(str) {
+ ch <- v
+ }
+ }()
+ return key_fingerprint_randomart(ch)
+}
+
+func FromFile(file *os.File) string {
+ ch := make(chan byte)
+
+ go func() {
+ defer close(ch)
+ // TODO make input a 1 element byte array
+ input := make([]byte, 1)
+ nread, err := file.Read(input)
+ for err == nil && nread > 0 {
+ ch <- input[0]
+ nread, err = file.Read(input)
+ }
+ }()
+ return key_fingerprint_randomart(ch)
+}
+
+func key_fingerprint_randomart(ch chan byte) string {
+ /*
+ * Chars to be used after each other every time the worm
+ * intersects with itself. Matter of taste.
+ */
+ augment_string := " .o+=*BOX@%/^SE"
+ var field [FLDSIZE_X][FLDSIZE_Y]byte
+ len_aug := len(augment_string) - 1
+ var retval [(FLDSIZE_X + 3) * (FLDSIZE_Y + 2)]byte
+
+ /* initialize field */
+ x := FLDSIZE_X / 2
+ y := FLDSIZE_Y / 2
+
+ /* process raw key */
+ for input, ok := <-ch; ok; input, ok = <-ch {
+ /* each byte conveys four 2-bit move commands */
+ for b := 0; b < 4; b++ {
+ /* evaluate 2 bit, rest is shifted later */
+ if input&0x1 > 0 {
+ x += 1
+ } else {
+ x += -1
+ }
+
+ if input&0x2 > 0 {
+ y++
+ } else {
+ y--
+ }
+
+ /* assure we are still in bounds */
+ x = MAX(x, 0)
+ y = MAX(y, 0)
+ x = MIN(x, FLDSIZE_X-1)
+ y = MIN(y, FLDSIZE_Y-1)
+
+ /* augment the field */
+ if int(field[x][y]) < len_aug-2 {
+ field[x][y]++
+ }
+ input = input >> 2
+ }
+ }
+
+ /* mark starting point and end point*/
+ field[FLDSIZE_X/2][FLDSIZE_Y/2] = byte(len_aug - 1)
+ field[x][y] = byte(len_aug)
+
+ i := 0
+ retval[i] = '+'
+ i++
+
+ /* output upper border */
+ for x := 0; x < FLDSIZE_X; x++ {
+ retval[i] = '-'
+ i++
+ }
+ retval[i] = '+'
+ i++
+ retval[i] = '\n'
+ i++
+
+ /* output content */
+ for y := 0; y < FLDSIZE_Y; y++ {
+ retval[i] = '|'
+ i++
+ for x := 0; x < FLDSIZE_X; x++ {
+ retval[i] = augment_string[MIN(int(field[x][y]), len_aug)]
+ i++
+ }
+ retval[i] = '|'
+ i++
+ retval[i] = '\n'
+ i++
+ }
+
+ /* output lower border */
+ retval[i] = '+'
+ i++
+ for j := 0; j < FLDSIZE_X; j++ {
+ retval[i] = '-'
+ i++
+ }
+ retval[i] = '+'
+ i++
+
+ return string(retval[0:i])
+}
diff --git a/appview/state/repo.go b/appview/state/repo.go
--- a/appview/state/repo.go
+++ b/appview/state/repo.go
@@ -35,19 +35,15 @@ if err != nil {
log.Println("failed to fully resolve repo", err)
return
}
- protocol := "http"
- if !s.config.Dev {
- protocol = "https"
- }
- var reqUrl string
- if ref != "" {
- reqUrl = fmt.Sprintf("%s://%s/%s/%s/tree/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref)
- } else {
- reqUrl = fmt.Sprintf("%s://%s/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName)
+ us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+ if err != nil {
+ log.Printf("failed to create unsigned client for %s", f.Knot)
+ s.pages.Error503(w)
+ return
}
- resp, err := http.Get(reqUrl)
+ resp, err := us.Index(f.OwnerDid(), f.RepoName, ref)
if err != nil {
s.pages.Error503(w)
log.Println("failed to reach knotserver", err)
@@ -303,9 +299,36 @@ }
switch r.Method {
case http.MethodGet:
+ us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+ if err != nil {
+ log.Printf("failed to create unsigned client for %s", f.Knot)
+ s.pages.Error503(w)
+ return
+ }
+
+ resp, err := us.Branches(f.OwnerDid(), f.RepoName)
+ if err != nil {
+ log.Println("failed to reach knotserver", err)
+ return
+ }
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Printf("Error reading response body: %v", err)
+ return
+ }
+
+ var result types.RepoBranchesResponse
+ err = json.Unmarshal(body, &result)
+ if err != nil {
+ log.Println("failed to parse response:", err)
+ return
+ }
+
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
+ Branches: result.Branches,
})
case http.MethodPost:
title := r.FormValue("title")
@@ -347,7 +370,7 @@ s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
return
}
client, _ := s.auth.AuthorizedClient(r)
- pullId, err := db.GetPullId(s.db, f.RepoAt)
+ pullId, err := db.NextPullId(s.db, f.RepoAt)
if err != nil {
log.Println("failed to get pull id", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
@@ -604,7 +627,13 @@ log.Println("failed to get repo and knot", err)
return
}
- resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/branches", f.Knot, f.OwnerDid(), f.RepoName))
+ us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+ if err != nil {
+ log.Println("failed to create unsigned client", err)
+ return
+ }
+
+ resp, err := us.Branches(f.OwnerDid(), f.RepoName)
if err != nil {
log.Println("failed to reach knotserver", err)
return
diff --git a/appview/state/signer.go b/appview/state/signer.go
--- a/appview/state/signer.go
+++ b/appview/state/signer.go
@@ -41,11 +41,11 @@ Secret: secret,
},
}
- uri := "https"
+ scheme := "https"
if dev {
- uri = "http"
+ scheme = "http"
}
- url, err := url.Parse(fmt.Sprintf("%s://%s", uri, domain))
+ url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
if err != nil {
return nil, err
}
@@ -193,3 +193,67 @@ }
return s.client.Do(req)
}
+
+type UnsignedClient struct {
+ Url *url.URL
+ client *http.Client
+}
+
+func NewUnsignedClient(domain string, dev bool) (*UnsignedClient, error) {
+ client := &http.Client{
+ Timeout: 5 * time.Second,
+ }
+
+ scheme := "https"
+ if dev {
+ scheme = "http"
+ }
+ url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
+ if err != nil {
+ return nil, err
+ }
+
+ unsignedClient := &UnsignedClient{
+ client: client,
+ Url: url,
+ }
+
+ return unsignedClient, nil
+}
+
+func (us *UnsignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
+ return http.NewRequest(method, us.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
+}
+
+func (us *UnsignedClient) Index(ownerDid, repoName, ref string) (*http.Response, error) {
+ const (
+ Method = "GET"
+ )
+
+ endpoint := fmt.Sprintf("/%s/%s/tree/%s", ownerDid, repoName, ref)
+ if ref == "" {
+ endpoint = fmt.Sprintf("/%s/%s", ownerDid, repoName)
+ }
+
+ req, err := us.newRequest(Method, endpoint, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return us.client.Do(req)
+}
+
+func (us *UnsignedClient) Branches(ownerDid, repoName string) (*http.Response, error) {
+ const (
+ Method = "GET"
+ )
+
+ endpoint := fmt.Sprintf("/%s/%s/branches", ownerDid, repoName)
+
+ req, err := us.newRequest(Method, endpoint, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return us.client.Do(req)
+}