diff --git a/README.md b/README.md
index dd948de..174ae3f 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
src="https://user-images.githubusercontent.com/6550035/31846899-2b8a7034-b5cf-11e7-9643-afe552226c59.png"
width="100%" border="0" alt="croc">
-
+
Easily and securely transfer stuff from one computer to another.
*croc* allows any two computers to directly and securely transfer files and folders. When sending a file, *croc* generates a random code phrase which must be shared with the recipient so they can receive the file. The code phrase encrypts all data and metadata and also serves to authorize the connection between the two computers in a intermediary relay. The relay connects the TCP ports between the two computers and does not store any information (and all information passing through it is encrypted). @@ -54,33 +54,6 @@ Received file written to some-file-or-folder (2.6 MB/s) Note, by default, you don't need any arguments for receiving! This makes it possible for you to just double click the executable to run (nice for those of us that aren't computer wizards). - -## Transfering files between local computers - -Its even easier if you want to transfer files between two computers on the same network. - -**Sender:** - -``` -$ croc -send some-file-or-folder -local -``` - -**Receiver:** - -``` -$ croc -local -``` - -Yes, when you run locally you don't even need to use a code. When you run locally, the *croc* receiver will use UDP broadcast packets to automatically find the correct IP address and code to use to transfer the file. When run locally, there is also no encryption so it is even faster. - -**Sender:** - -![Running locally](https://raw.githubusercontent.com/schollz/croc/master/logo/1.gif) - -**Receiver:** - -![Running locally](https://raw.githubusercontent.com/schollz/croc/master/logo/2.gif) - ## Using *croc* in pipes You can easily use *croc* in pipes when you need to send data through stdin or get data from stdout. @@ -111,15 +84,15 @@ Or, you can [install Go](https://golang.org/dl/) and build from source with `go # How does it work? -*croc* is similar to [magic-wormhole](https://github.com/warner/magic-wormhole#design) in spirit and design. Like *magic-wormhole*, *croc* generates a code phrase for you to share with your friend which allows secure end-to-end transfering of files and folders through a intermediary relay that connects the TCP ports between the two computers. +*croc* is similar to [magic-wormhole](https://github.com/warner/magic-wormhole#design) in spirit and design. Like *magic-wormhole*, *croc* generates a code phrase for you to share with your friend which allows secure end-to-end transfering of files and folders through a intermediary relay that connects the TCP ports between the two computers. The standard relay is on a public IP address (default `cowyo.com`), but before transmitting the file the two instances of *croc* send out UDP broadcasts to determine if they are both on the local network, and use a local relay instead of the cloud relay in the case that they are both local. -In *croc*, code phrase is 16 random bits that are [menemonic encoded](http://web.archive.org/web/20101031205747/http://www.tothink.com/mnemonic/). This code phrase is hashed using sha256 and sent to a relay which maps that key to that connection. When the relay finds a matching key for both the receiver and the sender (i.e. they both have the same code phrase), then the sender transmits the encrypted metadata to the receiver through the relay. Then the receiver decrypts and reviews the metadata (file name, size), and chooses whether to consent to the transfer. +The code phrase for transfering files is just three words which are 16 random bits that are [menemonic encoded](http://web.archive.org/web/20101031205747/http://www.tothink.com/mnemonic/). This code phrase is hashed using sha256 and sent to the relay which maps that hashed code phrase to that connection. When the relay finds a matching code phrase hash for both the receiver and the sender (i.e. they both have the same code phrase), then the sender transmits the encrypted metadata to the receiver through the relay. Then the receiver decrypts and reviews the metadata (file name, size), and chooses whether to consent to the transfer. After the receiver consents to the transfer, the sender transmits encrypted data through the relay. The relay setups up [Go channels](https://golang.org/doc/effective_go.html?h=chan#channels) for each connection which pipes all the data incoming from that sender's connection out to the receiver's connection. After the transmission the channels are destroyed and all the connection and meta data information is wiped from the relay server. The encrypted file data never is stored on the relay. **Encryption** -Encryption uses pbkdf2 (see [RFC2898](http://www.ietf.org/rfc/rfc2898.txt)) where the code phrase shared between the sender and receiver is used as the passphrase. For each of the two encrypted data blocks (metadata stored on relay server, and file data transmitted), a random 8-byte salt is used and a IV is generated according to [NIST Recommendation for Block ciphers, Section 8.2](http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf). +Encryption uses AES-256 with a pbkdf2 derived key (see [RFC2898](http://www.ietf.org/rfc/rfc2898.txt)) where the code phrase shared between the sender and receiver is used as the passphrase. For each of the two encrypted data blocks (metadata stored on relay server, and file data transmitted), a random 8-byte salt is used and a IV is generated according to [NIST Recommendation for Block ciphers, Section 8.2](http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf). **Decryption** diff --git a/testing_data/catFile1.txt b/testing_data/catFile1.txt deleted file mode 100644 index 0651332..0000000 --- a/testing_data/catFile1.txt +++ /dev/null @@ -1 +0,0 @@ -Some simple text to see if it works \ No newline at end of file diff --git a/testing_data/catFile2.txt b/testing_data/catFile2.txt deleted file mode 100644 index e9d648e..0000000 --- a/testing_data/catFile2.txt +++ /dev/null @@ -1 +0,0 @@ -More data to see if it 100% works \ No newline at end of file diff --git a/utils.go b/utils.go index 6da0ccb..5b4a99f 100644 --- a/utils.go +++ b/utils.go @@ -5,9 +5,11 @@ import ( "fmt" "io" "math" + math_rand "math/rand" "net" "os" "strconv" + "time" "github.com/pkg/errors" ) @@ -173,3 +175,32 @@ func GetLocalIP() string { } return bestIP } + +// src is seeds the random generator for generating random strings +var src = math_rand.NewSource(time.Now().UnixNano()) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<%RelFile
HTTP charset
- - - - - -The character encoding of a page can be set using the HTTP header charset declaration.
-The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ
. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.
-the-input-byte-stream-001
Result summary & related tests
Detailed results for this test
Link to spec
HTTP vs UTF-8 BOM
- - - - - -A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.
-The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè
. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
If the test is unsuccessful, the characters  should appear at the top of the page. These represent the bytes that make up the UTF-8 signature when encountered in the ISO 8859-15 encoding.
-the-input-byte-stream-034
Result summary & related tests
Detailed results for this test
Link to spec
HTTP vs meta charset
- - - - - -The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.
-The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-1.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ
. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
the-input-byte-stream-018
Result summary & related tests
Detailed results for this test
Link to spec
HTTP vs meta content
- - - - - -The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.
-The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-1.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ
. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
the-input-byte-stream-016
Result summary & related tests
Detailed results for this test
Link to spec
No encoding declaration
- - - - - -A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.
-The test on this page contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè
. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
the-input-byte-stream-015
Result summary & related tests
Detailed results for this test
Link to spec
UTF-8 BOM vs meta charset
- - - - - -A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.
-The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè
. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
the-input-byte-stream-038
Result summary & related tests
Detailed results for this test
Link to spec
UTF-8 BOM vs meta content
- - - - - -A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.
-The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè
. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
the-input-byte-stream-037
Result summary & related tests
Detailed results for this test
Link to spec
meta charset attribute
- - - - - -The character encoding of the page can be set by a meta element with charset attribute.
-The only character encoding declaration for this HTML file is in the charset attribute of the meta element, which declares the encoding to be ISO 8859-15.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ
. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
the-input-byte-stream-009
Result summary & related tests
Detailed results for this test
Link to spec
meta content attribute
- - - - - -The character encoding of the page can be set by a meta element with http-equiv and content attributes.
-The only character encoding declaration for this HTML file is in the content attribute of the meta element, which declares the encoding to be ISO 8859-15.
The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ
. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
the-input-byte-stream-007
Result summary & related tests
Detailed results for this test
Link to spec
Links:
` - doc, err := html.Parse(strings.NewReader(s)) - if err != nil { - log.Fatal(err) - } - var f func(*html.Node) - f = func(n *html.Node) { - if n.Type == html.ElementNode && n.Data == "a" { - for _, a := range n.Attr { - if a.Key == "href" { - fmt.Println(a.Val) - break - } - } - } - for c := n.FirstChild; c != nil; c = c.NextSibling { - f(c) - } - } - f(doc) - // Output: - // foo - // /bar/baz -} diff --git a/vendor/golang.org/x/net/html/foreign.go b/vendor/golang.org/x/net/html/foreign.go deleted file mode 100644 index 01477a9..0000000 --- a/vendor/golang.org/x/net/html/foreign.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2011 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 html - -import ( - "strings" -) - -func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { - for i := range aa { - if newName, ok := nameMap[aa[i].Key]; ok { - aa[i].Key = newName - } - } -} - -func adjustForeignAttributes(aa []Attribute) { - for i, a := range aa { - if a.Key == "" || a.Key[0] != 'x' { - continue - } - switch a.Key { - case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": - j := strings.Index(a.Key, ":") - aa[i].Namespace = a.Key[:j] - aa[i].Key = a.Key[j+1:] - } - } -} - -func htmlIntegrationPoint(n *Node) bool { - if n.Type != ElementNode { - return false - } - switch n.Namespace { - case "math": - if n.Data == "annotation-xml" { - for _, a := range n.Attr { - if a.Key == "encoding" { - val := strings.ToLower(a.Val) - if val == "text/html" || val == "application/xhtml+xml" { - return true - } - } - } - } - case "svg": - switch n.Data { - case "desc", "foreignObject", "title": - return true - } - } - return false -} - -func mathMLTextIntegrationPoint(n *Node) bool { - if n.Namespace != "math" { - return false - } - switch n.Data { - case "mi", "mo", "mn", "ms", "mtext": - return true - } - return false -} - -// Section 12.2.6.5. -var breakout = map[string]bool{ - "b": true, - "big": true, - "blockquote": true, - "body": true, - "br": true, - "center": true, - "code": true, - "dd": true, - "div": true, - "dl": true, - "dt": true, - "em": true, - "embed": true, - "h1": true, - "h2": true, - "h3": true, - "h4": true, - "h5": true, - "h6": true, - "head": true, - "hr": true, - "i": true, - "img": true, - "li": true, - "listing": true, - "menu": true, - "meta": true, - "nobr": true, - "ol": true, - "p": true, - "pre": true, - "ruby": true, - "s": true, - "small": true, - "span": true, - "strong": true, - "strike": true, - "sub": true, - "sup": true, - "table": true, - "tt": true, - "u": true, - "ul": true, - "var": true, -} - -// Section 12.2.6.5. -var svgTagNameAdjustments = map[string]string{ - "altglyph": "altGlyph", - "altglyphdef": "altGlyphDef", - "altglyphitem": "altGlyphItem", - "animatecolor": "animateColor", - "animatemotion": "animateMotion", - "animatetransform": "animateTransform", - "clippath": "clipPath", - "feblend": "feBlend", - "fecolormatrix": "feColorMatrix", - "fecomponenttransfer": "feComponentTransfer", - "fecomposite": "feComposite", - "feconvolvematrix": "feConvolveMatrix", - "fediffuselighting": "feDiffuseLighting", - "fedisplacementmap": "feDisplacementMap", - "fedistantlight": "feDistantLight", - "feflood": "feFlood", - "fefunca": "feFuncA", - "fefuncb": "feFuncB", - "fefuncg": "feFuncG", - "fefuncr": "feFuncR", - "fegaussianblur": "feGaussianBlur", - "feimage": "feImage", - "femerge": "feMerge", - "femergenode": "feMergeNode", - "femorphology": "feMorphology", - "feoffset": "feOffset", - "fepointlight": "fePointLight", - "fespecularlighting": "feSpecularLighting", - "fespotlight": "feSpotLight", - "fetile": "feTile", - "feturbulence": "feTurbulence", - "foreignobject": "foreignObject", - "glyphref": "glyphRef", - "lineargradient": "linearGradient", - "radialgradient": "radialGradient", - "textpath": "textPath", -} - -// Section 12.2.6.1 -var mathMLAttributeAdjustments = map[string]string{ - "definitionurl": "definitionURL", -} - -var svgAttributeAdjustments = map[string]string{ - "attributename": "attributeName", - "attributetype": "attributeType", - "basefrequency": "baseFrequency", - "baseprofile": "baseProfile", - "calcmode": "calcMode", - "clippathunits": "clipPathUnits", - "contentscripttype": "contentScriptType", - "contentstyletype": "contentStyleType", - "diffuseconstant": "diffuseConstant", - "edgemode": "edgeMode", - "externalresourcesrequired": "externalResourcesRequired", - "filterres": "filterRes", - "filterunits": "filterUnits", - "glyphref": "glyphRef", - "gradienttransform": "gradientTransform", - "gradientunits": "gradientUnits", - "kernelmatrix": "kernelMatrix", - "kernelunitlength": "kernelUnitLength", - "keypoints": "keyPoints", - "keysplines": "keySplines", - "keytimes": "keyTimes", - "lengthadjust": "lengthAdjust", - "limitingconeangle": "limitingConeAngle", - "markerheight": "markerHeight", - "markerunits": "markerUnits", - "markerwidth": "markerWidth", - "maskcontentunits": "maskContentUnits", - "maskunits": "maskUnits", - "numoctaves": "numOctaves", - "pathlength": "pathLength", - "patterncontentunits": "patternContentUnits", - "patterntransform": "patternTransform", - "patternunits": "patternUnits", - "pointsatx": "pointsAtX", - "pointsaty": "pointsAtY", - "pointsatz": "pointsAtZ", - "preservealpha": "preserveAlpha", - "preserveaspectratio": "preserveAspectRatio", - "primitiveunits": "primitiveUnits", - "refx": "refX", - "refy": "refY", - "repeatcount": "repeatCount", - "repeatdur": "repeatDur", - "requiredextensions": "requiredExtensions", - "requiredfeatures": "requiredFeatures", - "specularconstant": "specularConstant", - "specularexponent": "specularExponent", - "spreadmethod": "spreadMethod", - "startoffset": "startOffset", - "stddeviation": "stdDeviation", - "stitchtiles": "stitchTiles", - "surfacescale": "surfaceScale", - "systemlanguage": "systemLanguage", - "tablevalues": "tableValues", - "targetx": "targetX", - "targety": "targetY", - "textlength": "textLength", - "viewbox": "viewBox", - "viewtarget": "viewTarget", - "xchannelselector": "xChannelSelector", - "ychannelselector": "yChannelSelector", - "zoomandpan": "zoomAndPan", -} diff --git a/vendor/golang.org/x/net/html/node.go b/vendor/golang.org/x/net/html/node.go deleted file mode 100644 index 2c1cade..0000000 --- a/vendor/golang.org/x/net/html/node.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2011 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 html - -import ( - "golang.org/x/net/html/atom" -) - -// A NodeType is the type of a Node. -type NodeType uint32 - -const ( - ErrorNode NodeType = iota - TextNode - DocumentNode - ElementNode - CommentNode - DoctypeNode - scopeMarkerNode -) - -// Section 12.2.4.3 says "The markers are inserted when entering applet, -// object, marquee, template, td, th, and caption elements, and are used -// to prevent formatting from "leaking" into applet, object, marquee, -// template, td, th, and caption elements". -var scopeMarker = Node{Type: scopeMarkerNode} - -// A Node consists of a NodeType and some Data (tag name for element nodes, -// content for text) and are part of a tree of Nodes. Element nodes may also -// have a Namespace and contain a slice of Attributes. Data is unescaped, so -// that it looks like "a 0 { - return (*s)[i-1] - } - return nil -} - -// index returns the index of the top-most occurrence of n in the stack, or -1 -// if n is not present. -func (s *nodeStack) index(n *Node) int { - for i := len(*s) - 1; i >= 0; i-- { - if (*s)[i] == n { - return i - } - } - return -1 -} - -// contains returns whether a is within s. -func (s *nodeStack) contains(a atom.Atom) bool { - for _, n := range *s { - if n.DataAtom == a { - return true - } - } - return false -} - -// insert inserts a node at the given index. -func (s *nodeStack) insert(i int, n *Node) { - (*s) = append(*s, nil) - copy((*s)[i+1:], (*s)[i:]) - (*s)[i] = n -} - -// remove removes a node from the stack. It is a no-op if n is not present. -func (s *nodeStack) remove(n *Node) { - i := s.index(n) - if i == -1 { - return - } - copy((*s)[i:], (*s)[i+1:]) - j := len(*s) - 1 - (*s)[j] = nil - *s = (*s)[:j] -} - -type insertionModeStack []insertionMode - -func (s *insertionModeStack) pop() (im insertionMode) { - i := len(*s) - im = (*s)[i-1] - *s = (*s)[:i-1] - return im -} - -func (s *insertionModeStack) top() insertionMode { - if i := len(*s); i > 0 { - return (*s)[i-1] - } - return nil -} diff --git a/vendor/golang.org/x/net/html/node_test.go b/vendor/golang.org/x/net/html/node_test.go deleted file mode 100644 index 471102f..0000000 --- a/vendor/golang.org/x/net/html/node_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2010 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 html - -import ( - "fmt" -) - -// checkTreeConsistency checks that a node and its descendants are all -// consistent in their parent/child/sibling relationships. -func checkTreeConsistency(n *Node) error { - return checkTreeConsistency1(n, 0) -} - -func checkTreeConsistency1(n *Node, depth int) error { - if depth == 1e4 { - return fmt.Errorf("html: tree looks like it contains a cycle") - } - if err := checkNodeConsistency(n); err != nil { - return err - } - for c := n.FirstChild; c != nil; c = c.NextSibling { - if err := checkTreeConsistency1(c, depth+1); err != nil { - return err - } - } - return nil -} - -// checkNodeConsistency checks that a node's parent/child/sibling relationships -// are consistent. -func checkNodeConsistency(n *Node) error { - if n == nil { - return nil - } - - nParent := 0 - for p := n.Parent; p != nil; p = p.Parent { - nParent++ - if nParent == 1e4 { - return fmt.Errorf("html: parent list looks like an infinite loop") - } - } - - nForward := 0 - for c := n.FirstChild; c != nil; c = c.NextSibling { - nForward++ - if nForward == 1e6 { - return fmt.Errorf("html: forward list of children looks like an infinite loop") - } - if c.Parent != n { - return fmt.Errorf("html: inconsistent child/parent relationship") - } - } - - nBackward := 0 - for c := n.LastChild; c != nil; c = c.PrevSibling { - nBackward++ - if nBackward == 1e6 { - return fmt.Errorf("html: backward list of children looks like an infinite loop") - } - if c.Parent != n { - return fmt.Errorf("html: inconsistent child/parent relationship") - } - } - - if n.Parent != nil { - if n.Parent == n { - return fmt.Errorf("html: inconsistent parent relationship") - } - if n.Parent == n.FirstChild { - return fmt.Errorf("html: inconsistent parent/first relationship") - } - if n.Parent == n.LastChild { - return fmt.Errorf("html: inconsistent parent/last relationship") - } - if n.Parent == n.PrevSibling { - return fmt.Errorf("html: inconsistent parent/prev relationship") - } - if n.Parent == n.NextSibling { - return fmt.Errorf("html: inconsistent parent/next relationship") - } - - parentHasNAsAChild := false - for c := n.Parent.FirstChild; c != nil; c = c.NextSibling { - if c == n { - parentHasNAsAChild = true - break - } - } - if !parentHasNAsAChild { - return fmt.Errorf("html: inconsistent parent/child relationship") - } - } - - if n.PrevSibling != nil && n.PrevSibling.NextSibling != n { - return fmt.Errorf("html: inconsistent prev/next relationship") - } - if n.NextSibling != nil && n.NextSibling.PrevSibling != n { - return fmt.Errorf("html: inconsistent next/prev relationship") - } - - if (n.FirstChild == nil) != (n.LastChild == nil) { - return fmt.Errorf("html: inconsistent first/last relationship") - } - if n.FirstChild != nil && n.FirstChild == n.LastChild { - // We have a sole child. - if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil { - return fmt.Errorf("html: inconsistent sole child's sibling relationship") - } - } - - seen := map[*Node]bool{} - - var last *Node - for c := n.FirstChild; c != nil; c = c.NextSibling { - if seen[c] { - return fmt.Errorf("html: inconsistent repeated child") - } - seen[c] = true - last = c - } - if last != n.LastChild { - return fmt.Errorf("html: inconsistent last relationship") - } - - var first *Node - for c := n.LastChild; c != nil; c = c.PrevSibling { - if !seen[c] { - return fmt.Errorf("html: inconsistent missing child") - } - delete(seen, c) - first = c - } - if first != n.FirstChild { - return fmt.Errorf("html: inconsistent first relationship") - } - - if len(seen) != 0 { - return fmt.Errorf("html: inconsistent forwards/backwards child list") - } - - return nil -} diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go deleted file mode 100644 index d23e05e..0000000 --- a/vendor/golang.org/x/net/html/parse.go +++ /dev/null @@ -1,2305 +0,0 @@ -// Copyright 2010 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 html - -import ( - "errors" - "fmt" - "io" - "strings" - - a "golang.org/x/net/html/atom" -) - -// A parser implements the HTML5 parsing algorithm: -// https://html.spec.whatwg.org/multipage/syntax.html#tree-construction -type parser struct { - // tokenizer provides the tokens for the parser. - tokenizer *Tokenizer - // tok is the most recently read token. - tok Token - // Self-closing tags likeblock. - if d != "" && d[0] == '\r' { - d = d[1:] - } - if d != "" && d[0] == '\n' { - d = d[1:] - } - } - } - d = strings.Replace(d, "\x00", "", -1) - if d == "" { - return true - } - p.reconstructActiveFormattingElements() - p.addText(d) - if p.framesetOK && strings.TrimLeft(d, whitespace) != "" { - // There were non-whitespace characters inserted. - p.framesetOK = false - } - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - if p.oe.contains(a.Template) { - return true - } - copyAttributes(p.oe[0], p.tok) - case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: - return inHeadIM(p) - case a.Body: - if p.oe.contains(a.Template) { - return true - } - if len(p.oe) >= 2 { - body := p.oe[1] - if body.Type == ElementNode && body.DataAtom == a.Body { - p.framesetOK = false - copyAttributes(body, p.tok) - } - } - case a.Frameset: - if !p.framesetOK || len(p.oe) < 2 || p.oe[1].DataAtom != a.Body { - // Ignore the token. - return true - } - body := p.oe[1] - if body.Parent != nil { - body.Parent.RemoveChild(body) - } - p.oe = p.oe[:1] - p.addElement() - p.im = inFramesetIM - return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul: - p.popUntil(buttonScope, a.P) - p.addElement() - case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: - p.popUntil(buttonScope, a.P) - switch n := p.top(); n.DataAtom { - case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: - p.oe.pop() - } - p.addElement() - case a.Pre, a.Listing: - p.popUntil(buttonScope, a.P) - p.addElement() - // The newline, if any, will be dealt with by the TextToken case. - p.framesetOK = false - case a.Form: - if p.oe.contains(a.Template) || p.form == nil { - p.popUntil(buttonScope, a.P) - p.addElement() - p.form = p.top() - } - case a.Li: - p.framesetOK = false - for i := len(p.oe) - 1; i >= 0; i-- { - node := p.oe[i] - switch node.DataAtom { - case a.Li: - p.oe = p.oe[:i] - case a.Address, a.Div, a.P: - continue - default: - if !isSpecialElement(node) { - continue - } - } - break - } - p.popUntil(buttonScope, a.P) - p.addElement() - case a.Dd, a.Dt: - p.framesetOK = false - for i := len(p.oe) - 1; i >= 0; i-- { - node := p.oe[i] - switch node.DataAtom { - case a.Dd, a.Dt: - p.oe = p.oe[:i] - case a.Address, a.Div, a.P: - continue - default: - if !isSpecialElement(node) { - continue - } - } - break - } - p.popUntil(buttonScope, a.P) - p.addElement() - case a.Plaintext: - p.popUntil(buttonScope, a.P) - p.addElement() - case a.Button: - p.popUntil(defaultScope, a.Button) - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - case a.A: - for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- { - if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A { - p.inBodyEndTagFormatting(a.A) - p.oe.remove(n) - p.afe.remove(n) - break - } - } - p.reconstructActiveFormattingElements() - p.addFormattingElement() - case a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U: - p.reconstructActiveFormattingElements() - p.addFormattingElement() - case a.Nobr: - p.reconstructActiveFormattingElements() - if p.elementInScope(defaultScope, a.Nobr) { - p.inBodyEndTagFormatting(a.Nobr) - p.reconstructActiveFormattingElements() - } - p.addFormattingElement() - case a.Applet, a.Marquee, a.Object: - p.reconstructActiveFormattingElements() - p.addElement() - p.afe = append(p.afe, &scopeMarker) - p.framesetOK = false - case a.Table: - if !p.quirks { - p.popUntil(buttonScope, a.P) - } - p.addElement() - p.framesetOK = false - p.im = inTableIM - return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: - p.reconstructActiveFormattingElements() - p.addElement() - p.oe.pop() - p.acknowledgeSelfClosingTag() - if p.tok.DataAtom == a.Input { - for _, t := range p.tok.Attr { - if t.Key == "type" { - if strings.ToLower(t.Val) == "hidden" { - // Skip setting framesetOK = false - return true - } - } - } - } - p.framesetOK = false - case a.Param, a.Source, a.Track: - p.addElement() - p.oe.pop() - p.acknowledgeSelfClosingTag() - case a.Hr: - p.popUntil(buttonScope, a.P) - p.addElement() - p.oe.pop() - p.acknowledgeSelfClosingTag() - p.framesetOK = false - case a.Image: - p.tok.DataAtom = a.Img - p.tok.Data = a.Img.String() - return false - case a.Isindex: - if p.form != nil { - // Ignore the token. - return true - } - action := "" - prompt := "This is a searchable index. Enter search keywords: " - attr := []Attribute{{Key: "name", Val: "isindex"}} - for _, t := range p.tok.Attr { - switch t.Key { - case "action": - action = t.Val - case "name": - // Ignore the attribute. - case "prompt": - prompt = t.Val - default: - attr = append(attr, t) - } - } - p.acknowledgeSelfClosingTag() - p.popUntil(buttonScope, a.P) - p.parseImpliedToken(StartTagToken, a.Form, a.Form.String()) - if action != "" { - p.form.Attr = []Attribute{{Key: "action", Val: action}} - } - p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String()) - p.parseImpliedToken(StartTagToken, a.Label, a.Label.String()) - p.addText(prompt) - p.addChild(&Node{ - Type: ElementNode, - DataAtom: a.Input, - Data: a.Input.String(), - Attr: attr, - }) - p.oe.pop() - p.parseImpliedToken(EndTagToken, a.Label, a.Label.String()) - p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String()) - p.parseImpliedToken(EndTagToken, a.Form, a.Form.String()) - case a.Textarea: - p.addElement() - p.setOriginalIM() - p.framesetOK = false - p.im = textIM - case a.Xmp: - p.popUntil(buttonScope, a.P) - p.reconstructActiveFormattingElements() - p.framesetOK = false - p.addElement() - p.setOriginalIM() - p.im = textIM - case a.Iframe: - p.framesetOK = false - p.addElement() - p.setOriginalIM() - p.im = textIM - case a.Noembed, a.Noscript: - p.addElement() - p.setOriginalIM() - p.im = textIM - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectIM - return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.reconstructActiveFormattingElements() - p.addElement() - case a.Rb, a.Rtc: - if p.elementInScope(defaultScope, a.Ruby) { - p.generateImpliedEndTags() - } - p.addElement() - case a.Rp, a.Rt: - if p.elementInScope(defaultScope, a.Ruby) { - p.generateImpliedEndTags("rtc") - } - p.addElement() - case a.Math, a.Svg: - p.reconstructActiveFormattingElements() - if p.tok.DataAtom == a.Math { - adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments) - } else { - adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments) - } - adjustForeignAttributes(p.tok.Attr) - p.addElement() - p.top().Namespace = p.tok.Data - if p.hasSelfClosingToken { - p.oe.pop() - p.acknowledgeSelfClosingTag() - } - return true - case a.Frame: - // TODO: remove this divergence from the HTML5 spec. - if p.oe.contains(a.Template) { - p.addElement() - return true - } - case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: - // Ignore the token. - default: - p.reconstructActiveFormattingElements() - p.addElement() - } - case EndTagToken: - switch p.tok.DataAtom { - case a.Body: - if p.elementInScope(defaultScope, a.Body) { - p.im = afterBodyIM - } - case a.Html: - if p.elementInScope(defaultScope, a.Body) { - p.parseImpliedToken(EndTagToken, a.Body, a.Body.String()) - return false - } - return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul: - p.popUntil(defaultScope, p.tok.DataAtom) - case a.Form: - if p.oe.contains(a.Template) { - if !p.oe.contains(a.Form) { - // Ignore the token. - return true - } - p.generateImpliedEndTags() - if p.tok.DataAtom == a.Form { - // Ignore the token. - return true - } - p.popUntil(defaultScope, a.Form) - } else { - node := p.form - p.form = nil - i := p.indexOfElementInScope(defaultScope, a.Form) - if node == nil || i == -1 || p.oe[i] != node { - // Ignore the token. - return true - } - p.generateImpliedEndTags() - p.oe.remove(node) - } - case a.P: - if !p.elementInScope(buttonScope, a.P) { - p.parseImpliedToken(StartTagToken, a.P, a.P.String()) - } - p.popUntil(buttonScope, a.P) - case a.Li: - p.popUntil(listItemScope, a.Li) - case a.Dd, a.Dt: - p.popUntil(defaultScope, p.tok.DataAtom) - case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: - p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6) - case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U: - p.inBodyEndTagFormatting(p.tok.DataAtom) - case a.Applet, a.Marquee, a.Object: - if p.popUntil(defaultScope, p.tok.DataAtom) { - p.clearActiveFormattingElements() - } - case a.Br: - p.tok.Type = StartTagToken - return false - case a.Template: - return inHeadIM(p) - default: - p.inBodyEndTagOther(p.tok.DataAtom) - } - case CommentToken: - p.addChild(&Node{ - Type: CommentNode, - Data: p.tok.Data, - }) - case ErrorToken: - // TODO: remove this divergence from the HTML5 spec. - if len(p.templateStack) > 0 { - p.im = inTemplateIM - return false - } else { - for _, e := range p.oe { - switch e.DataAtom { - case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th, - a.Thead, a.Tr, a.Body, a.Html: - default: - return true - } - } - } - } - - return true -} - -func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { - // This is the "adoption agency" algorithm, described at - // https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency - - // TODO: this is a fairly literal line-by-line translation of that algorithm. - // Once the code successfully parses the comprehensive test suite, we should - // refactor this code to be more idiomatic. - - // Steps 1-4. The outer loop. - for i := 0; i < 8; i++ { - // Step 5. Find the formatting element. - var formattingElement *Node - for j := len(p.afe) - 1; j >= 0; j-- { - if p.afe[j].Type == scopeMarkerNode { - break - } - if p.afe[j].DataAtom == tagAtom { - formattingElement = p.afe[j] - break - } - } - if formattingElement == nil { - p.inBodyEndTagOther(tagAtom) - return - } - feIndex := p.oe.index(formattingElement) - if feIndex == -1 { - p.afe.remove(formattingElement) - return - } - if !p.elementInScope(defaultScope, tagAtom) { - // Ignore the tag. - return - } - - // Steps 9-10. Find the furthest block. - var furthestBlock *Node - for _, e := range p.oe[feIndex:] { - if isSpecialElement(e) { - furthestBlock = e - break - } - } - if furthestBlock == nil { - e := p.oe.pop() - for e != formattingElement { - e = p.oe.pop() - } - p.afe.remove(e) - return - } - - // Steps 11-12. Find the common ancestor and bookmark node. - commonAncestor := p.oe[feIndex-1] - bookmark := p.afe.index(formattingElement) - - // Step 13. The inner loop. Find the lastNode to reparent. - lastNode := furthestBlock - node := furthestBlock - x := p.oe.index(node) - // Steps 13.1-13.2 - for j := 0; j < 3; j++ { - // Step 13.3. - x-- - node = p.oe[x] - // Step 13.4 - 13.5. - if p.afe.index(node) == -1 { - p.oe.remove(node) - continue - } - // Step 13.6. - if node == formattingElement { - break - } - // Step 13.7. - clone := node.clone() - p.afe[p.afe.index(node)] = clone - p.oe[p.oe.index(node)] = clone - node = clone - // Step 13.8. - if lastNode == furthestBlock { - bookmark = p.afe.index(node) + 1 - } - // Step 13.9. - if lastNode.Parent != nil { - lastNode.Parent.RemoveChild(lastNode) - } - node.AppendChild(lastNode) - // Step 13.10. - lastNode = node - } - - // Step 14. Reparent lastNode to the common ancestor, - // or for misnested table nodes, to the foster parent. - if lastNode.Parent != nil { - lastNode.Parent.RemoveChild(lastNode) - } - switch commonAncestor.DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParent(lastNode) - case a.Template: - // TODO: remove namespace checking - if commonAncestor.Namespace == "html" { - commonAncestor = commonAncestor.LastChild - } - fallthrough - default: - commonAncestor.AppendChild(lastNode) - } - - // Steps 15-17. Reparent nodes from the furthest block's children - // to a clone of the formatting element. - clone := formattingElement.clone() - reparentChildren(clone, furthestBlock) - furthestBlock.AppendChild(clone) - - // Step 18. Fix up the list of active formatting elements. - if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark { - // Move the bookmark with the rest of the list. - bookmark-- - } - p.afe.remove(formattingElement) - p.afe.insert(bookmark, clone) - - // Step 19. Fix up the stack of open elements. - p.oe.remove(formattingElement) - p.oe.insert(p.oe.index(furthestBlock)+1, clone) - } -} - -// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. -// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content -// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign -func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { - for i := len(p.oe) - 1; i >= 0; i-- { - if p.oe[i].DataAtom == tagAtom { - p.oe = p.oe[:i] - break - } - if isSpecialElement(p.oe[i]) { - break - } - } -} - -// Section 12.2.6.4.8. -func textIM(p *parser) bool { - switch p.tok.Type { - case ErrorToken: - p.oe.pop() - case TextToken: - d := p.tok.Data - if n := p.oe.top(); n.DataAtom == a.Textarea && n.FirstChild == nil { - // Ignore a newline at the start of a