Artifact 40de757881fb336a7b593186b694b8e932c081d797b721ed99c8649fd3195878:
- File
internal/sync/version.go
— part of check-in
[9d35c01fb3]
at
2026-10-13 00:12:32
on branch main
— fix(sync): log in through the x-f-l-c cookie and accept cleartext passwords (#304) (#362)
fossil's client moves its login card from the body into the x-f-l-c cookie once the server's server-version passes xfer_xflc_check (2.27, 2025-07-27 11:05 or later), and fossil's check_login, when a stored password is not 40 characters, tries the signature again against the shared secret that cleartext password derives (sections 6.4, 6.5). The client always logged in in the body, and the server checked only the stored value.
A request marks itself LoginCookieOK once the gate passes; Message.SplitLoginCookie moves its login card out of the body, and the HTTP transports (sync.HTTPTransport and the public NewHTTPTransport through the adapter) send it as the cookie, on every redirect too. The nonce is unchanged: it already covers what follows the login line, which in cookie form is the whole body. Transports that cannot set a cookie send the login in the body as before. The server side of the cookie landed with the server-version exchange.
HandleOpts gains LoginCookie, so an embedder serving HandleSyncWithOpts over its own HTTP handler can pass the cookie through; XferHandler and ServeHTTP read it themselves. (user: [email protected], size: 2459) [annotate] [blame] [check-ins using]
package sync import ( "strconv" "github.com/danmestas/go-libfossil/internal/xfer" ) // The release this library announces in pragma client-version and // server-version (§9.1): the fossil release whose protocol it speaks, and // that release's check-in date and time. const ( releaseVersion = 22800 releaseDate = "20260315" releaseTime = "120000" ) // sha3Version is the first fossil release that accepts SHA3 artifact names. // Content named by SHA3 is withheld from a peer older than this (§7.6). const sha3Version = 20000 // sha1NameLen is the length of a SHA1 artifact name; any longer name is SHA3. const sha1NameLen = 40 // versionPragma is the pragma that announces this library's release: name is // "client-version" or "server-version". func versionPragma(name string) *xfer.PragmaCard { return &xfer.PragmaCard{ Name: name, Values: []string{strconv.Itoa(releaseVersion), releaseDate, releaseTime}, } } // peerVersion reads the VERSION of a client-version or server-version pragma // as fossil's atoi does: leading digits, and 0 when there are none. func peerVersion(c *xfer.PragmaCard) int { if len(c.Values) == 0 { panic("sync.peerVersion: pragma has no VERSION") } v := c.Values[0] n := 0 for n < len(v) && v[n] >= '0' && v[n] <= '9' { n++ } version, err := strconv.Atoi(v[:n]) if err != nil { return 0 } return version } // loginCookieGate reports whether a server-version names a release that // reads a login card from the x-f-l-c cookie: 2.27 or later, built at or // after 2025-07-27 11:05:00, as fossil's xfer_xflc_check decides (§6.5). A // server-version without DATE and TIME does not pass. func loginCookieGate(c *xfer.PragmaCard) bool { if len(c.Values) < 3 { return false } date, _ := strconv.Atoi(c.Values[1]) clock, _ := strconv.Atoi(c.Values[2]) return peerVersion(c) >= 22700 && (date > 20250727 || (date == 20250727 && clock >= 110500)) } // sha3Barred reports whether content named name is withheld from a peer of // the given release: a SHA3 name, to a peer older than SHA3 support (§7.6). func sha3Barred(name string, version int) bool { return len(name) > sha1NameLen && version < sha3Version } // sha3Refusal is the error card that takes the place of SHA3 content withheld // from an older peer, worded as fossil's xfer_cannot_send_sha3_error words it. func sha3Refusal() *xfer.ErrorCard { return &xfer.ErrorCard{Message: "Fossil version 2.0 or later required."} }