diff --git a/go.mod b/go.mod index 4f45e8c3..545f4c9e 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/hashicorp/nomad/api v0.0.0-20250812204832-62b195aaa535 // v1.10.4 github.com/jedib0t/go-pretty/v6 v6.6.8 github.com/matcornic/hermes/v2 v2.1.0 - github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 github.com/microcosm-cc/bluemonday v1.0.27 github.com/moby/buildkit v0.23.2 github.com/nlopes/slack v0.6.0 @@ -40,18 +39,20 @@ require ( github.com/tidwall/pretty v1.2.1 go.etcd.io/bbolt v1.4.3 go.podman.io/image/v5 v5.37.0 - golang.org/x/mod v0.27.0 - golang.org/x/sys v0.35.0 + golang.org/x/mod v0.29.0 + golang.org/x/sys v0.38.0 google.golang.org/grpc v1.74.2 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 - google.golang.org/protobuf v1.36.8 + google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.33.3 k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.3 + maunium.net/go/mautrix v0.25.2 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect @@ -104,8 +105,8 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -124,22 +125,27 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.2.0 // indirect + github.com/tidwall/sjson v1.2.5 // indirect github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect github.com/x448/float16 v0.8.4 // indirect + go.mau.fi/util v0.9.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel/metric v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect go.podman.io/storage v1.60.0 // indirect - golang.org/x/crypto v0.41.0 // indirect - golang.org/x/net v0.43.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect + golang.org/x/net v0.46.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/term v0.36.0 // indirect + golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.11.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index 42224ddb..35017cad 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -218,15 +220,15 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc= github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI= -github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4= -github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -332,8 +334,17 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE= github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= @@ -351,6 +362,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= +go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= @@ -382,14 +395,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -402,8 +417,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -411,8 +426,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -432,14 +447,14 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -447,8 +462,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -457,8 +472,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -472,8 +487,8 @@ google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -506,6 +521,8 @@ k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUy k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +maunium.net/go/mautrix v0.25.2 h1:CUG23zp754yGOTMh9Q4mVSENS9FyweE/G+6ZsPDMCUU= +maunium.net/go/mautrix v0.25.2/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= diff --git a/internal/notif/matrix/client.go b/internal/notif/matrix/client.go index 37be85a5..cb89b28c 100644 --- a/internal/notif/matrix/client.go +++ b/internal/notif/matrix/client.go @@ -1,15 +1,17 @@ package matrix import ( + "context" "fmt" "github.com/crazy-max/diun/v4/internal/model" "github.com/crazy-max/diun/v4/internal/msg" "github.com/crazy-max/diun/v4/internal/notif/notifier" "github.com/crazy-max/diun/v4/pkg/utl" - "github.com/matrix-org/gomatrix" "github.com/pkg/errors" "github.com/rs/zerolog/log" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" ) // Client represents an active rocketchat notification object @@ -36,12 +38,13 @@ func (c *Client) Name() string { // Send creates and sends a matrix notification with an entry func (c *Client) Send(entry model.NotifEntry) error { - m, err := gomatrix.NewClient(c.cfg.HomeserverURL, "", "") + ctx := context.Background() + m, err := mautrix.NewClient(c.cfg.HomeserverURL, "", "") if err != nil { return errors.Wrap(err, "failed to initialize Matrix client") } defer func() { - if _, err := m.Logout(); err != nil { + if _, err := m.Logout(ctx); err != nil { log.Error().Err(err).Msg("Cannot logout") } }() @@ -55,11 +58,11 @@ func (c *Client) Send(entry model.NotifEntry) error { return errors.Wrap(err, "cannot retrieve password secret for Matrix notifier") } - r, err := m.Login(&gomatrix.ReqLogin{ + _, err = m.Login(ctx, &mautrix.ReqLogin{ Type: "m.login.password", - Identifier: gomatrix.UserIdentifier{ - IDType: "m.id.user", - User: user, + Identifier: mautrix.UserIdentifier{ + Type: "m.id.user", + User: user, }, Password: password, InitialDeviceDisplayName: c.meta.Name, @@ -67,9 +70,8 @@ func (c *Client) Send(entry model.NotifEntry) error { if err != nil { return errors.Wrap(err, "failed to authenticate Matrix user") } - m.SetCredentials(r.UserID, r.AccessToken) - joined, err := m.JoinRoom(c.cfg.RoomID, "", nil) + joined, err := m.JoinRoom(ctx, c.cfg.RoomID, &mautrix.ReqJoinRoom{}) if err != nil { return errors.Wrap(err, "failed to join room") } @@ -93,14 +95,25 @@ func (c *Client) Send(entry model.NotifEntry) error { return err } - if _, err := m.SendMessageEvent(joined.RoomID, "m.room.message", gomatrix.HTMLMessage{ - Body: string(msgText), + type MatrixMessage struct { + Body string `json:"body"` + MsgType string `json:"msgtype"` + Format string `json:"format"` + FormattedBody string `json:"formatted_body"` + } + + eventType := event.Type{ + Type: "m.room.message", + Class: event.MessageEventType, + } + + if _, err := m.SendMessageEvent(ctx, joined.RoomID, eventType, MatrixMessage{ MsgType: fmt.Sprintf("m.%s", c.cfg.MsgType), + Body: string(msgText), Format: "org.matrix.custom.html", FormattedBody: string(msgHTML), }); err != nil { return errors.Wrap(err, "failed to submit message to Matrix") } - return nil } diff --git a/pb/image.pb.go b/pb/image.pb.go index e3faf614..7f2c8373 100644 --- a/pb/image.pb.go +++ b/pb/image.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.8 +// protoc-gen-go v1.36.10 // protoc v3.17.3 // source: image.proto diff --git a/pb/notif.pb.go b/pb/notif.pb.go index 36532fcf..f18d1a2e 100644 --- a/pb/notif.pb.go +++ b/pb/notif.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.8 +// protoc-gen-go v1.36.10 // protoc v3.17.3 // source: notif.proto diff --git a/vendor/filippo.io/edwards25519/LICENSE b/vendor/filippo.io/edwards25519/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/filippo.io/edwards25519/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/filippo.io/edwards25519/README.md b/vendor/filippo.io/edwards25519/README.md new file mode 100644 index 00000000..24e2457d --- /dev/null +++ b/vendor/filippo.io/edwards25519/README.md @@ -0,0 +1,14 @@ +# filippo.io/edwards25519 + +``` +import "filippo.io/edwards25519" +``` + +This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives. +Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519). + +The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality. + +Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/internal/edwards25519`/`crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative. + +Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements. diff --git a/vendor/filippo.io/edwards25519/doc.go b/vendor/filippo.io/edwards25519/doc.go new file mode 100644 index 00000000..ab6aaebc --- /dev/null +++ b/vendor/filippo.io/edwards25519/doc.go @@ -0,0 +1,20 @@ +// Copyright (c) 2021 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 edwards25519 implements group logic for the twisted Edwards curve +// +// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2 +// +// This is better known as the Edwards curve equivalent to Curve25519, and is +// the curve used by the Ed25519 signature scheme. +// +// Most users don't need this package, and should instead use crypto/ed25519 for +// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or +// github.com/gtank/ristretto255 for prime order group logic. +// +// However, developers who do need to interact with low-level edwards25519 +// operations can use this package, which is an extended version of +// crypto/internal/edwards25519 from the standard library repackaged as +// an importable module. +package edwards25519 diff --git a/vendor/filippo.io/edwards25519/edwards25519.go b/vendor/filippo.io/edwards25519/edwards25519.go new file mode 100644 index 00000000..a744da2c --- /dev/null +++ b/vendor/filippo.io/edwards25519/edwards25519.go @@ -0,0 +1,427 @@ +// Copyright (c) 2017 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 edwards25519 + +import ( + "errors" + + "filippo.io/edwards25519/field" +) + +// Point types. + +type projP1xP1 struct { + X, Y, Z, T field.Element +} + +type projP2 struct { + X, Y, Z field.Element +} + +// Point represents a point on the edwards25519 curve. +// +// This type works similarly to math/big.Int, and all arguments and receivers +// are allowed to alias. +// +// The zero value is NOT valid, and it may be used only as a receiver. +type Point struct { + // Make the type not comparable (i.e. used with == or as a map key), as + // equivalent points can be represented by different Go values. + _ incomparable + + // The point is internally represented in extended coordinates (X, Y, Z, T) + // where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522. + x, y, z, t field.Element +} + +type incomparable [0]func() + +func checkInitialized(points ...*Point) { + for _, p := range points { + if p.x == (field.Element{}) && p.y == (field.Element{}) { + panic("edwards25519: use of uninitialized Point") + } + } +} + +type projCached struct { + YplusX, YminusX, Z, T2d field.Element +} + +type affineCached struct { + YplusX, YminusX, T2d field.Element +} + +// Constructors. + +func (v *projP2) Zero() *projP2 { + v.X.Zero() + v.Y.One() + v.Z.One() + return v +} + +// identity is the point at infinity. +var identity, _ = new(Point).SetBytes([]byte{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + +// NewIdentityPoint returns a new Point set to the identity. +func NewIdentityPoint() *Point { + return new(Point).Set(identity) +} + +// generator is the canonical curve basepoint. See TestGenerator for the +// correspondence of this encoding with the values in RFC 8032. +var generator, _ = new(Point).SetBytes([]byte{ + 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}) + +// NewGeneratorPoint returns a new Point set to the canonical generator. +func NewGeneratorPoint() *Point { + return new(Point).Set(generator) +} + +func (v *projCached) Zero() *projCached { + v.YplusX.One() + v.YminusX.One() + v.Z.One() + v.T2d.Zero() + return v +} + +func (v *affineCached) Zero() *affineCached { + v.YplusX.One() + v.YminusX.One() + v.T2d.Zero() + return v +} + +// Assignments. + +// Set sets v = u, and returns v. +func (v *Point) Set(u *Point) *Point { + *v = *u + return v +} + +// Encoding. + +// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032, +// Section 5.1.2. +func (v *Point) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var buf [32]byte + return v.bytes(&buf) +} + +func (v *Point) bytes(buf *[32]byte) []byte { + checkInitialized(v) + + var zInv, x, y field.Element + zInv.Invert(&v.z) // zInv = 1 / Z + x.Multiply(&v.x, &zInv) // x = X / Z + y.Multiply(&v.y, &zInv) // y = Y / Z + + out := copyFieldElement(buf, &y) + out[31] |= byte(x.IsNegative() << 7) + return out +} + +var feOne = new(field.Element).One() + +// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not +// represent a valid point on the curve, SetBytes returns nil and an error and +// the receiver is unchanged. Otherwise, SetBytes returns v. +// +// Note that SetBytes accepts all non-canonical encodings of valid points. +// That is, it follows decoding rules that match most implementations in +// the ecosystem rather than RFC 8032. +func (v *Point) SetBytes(x []byte) (*Point, error) { + // Specifically, the non-canonical encodings that are accepted are + // 1) the ones where the field element is not reduced (see the + // (*field.Element).SetBytes docs) and + // 2) the ones where the x-coordinate is zero and the sign bit is set. + // + // Read more at https://hdevalence.ca/blog/2020-10-04-its-25519am, + // specifically the "Canonical A, R" section. + + y, err := new(field.Element).SetBytes(x) + if err != nil { + return nil, errors.New("edwards25519: invalid point encoding length") + } + + // -x² + y² = 1 + dx²y² + // x² + dx²y² = x²(dy² + 1) = y² - 1 + // x² = (y² - 1) / (dy² + 1) + + // u = y² - 1 + y2 := new(field.Element).Square(y) + u := new(field.Element).Subtract(y2, feOne) + + // v = dy² + 1 + vv := new(field.Element).Multiply(y2, d) + vv = vv.Add(vv, feOne) + + // x = +√(u/v) + xx, wasSquare := new(field.Element).SqrtRatio(u, vv) + if wasSquare == 0 { + return nil, errors.New("edwards25519: invalid point encoding") + } + + // Select the negative square root if the sign bit is set. + xxNeg := new(field.Element).Negate(xx) + xx = xx.Select(xxNeg, xx, int(x[31]>>7)) + + v.x.Set(xx) + v.y.Set(y) + v.z.One() + v.t.Multiply(xx, y) // xy = T / Z + + return v, nil +} + +func copyFieldElement(buf *[32]byte, v *field.Element) []byte { + copy(buf[:], v.Bytes()) + return buf[:] +} + +// Conversions. + +func (v *projP2) FromP1xP1(p *projP1xP1) *projP2 { + v.X.Multiply(&p.X, &p.T) + v.Y.Multiply(&p.Y, &p.Z) + v.Z.Multiply(&p.Z, &p.T) + return v +} + +func (v *projP2) FromP3(p *Point) *projP2 { + v.X.Set(&p.x) + v.Y.Set(&p.y) + v.Z.Set(&p.z) + return v +} + +func (v *Point) fromP1xP1(p *projP1xP1) *Point { + v.x.Multiply(&p.X, &p.T) + v.y.Multiply(&p.Y, &p.Z) + v.z.Multiply(&p.Z, &p.T) + v.t.Multiply(&p.X, &p.Y) + return v +} + +func (v *Point) fromP2(p *projP2) *Point { + v.x.Multiply(&p.X, &p.Z) + v.y.Multiply(&p.Y, &p.Z) + v.z.Square(&p.Z) + v.t.Multiply(&p.X, &p.Y) + return v +} + +// d is a constant in the curve equation. +var d, _ = new(field.Element).SetBytes([]byte{ + 0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, + 0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00, + 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, + 0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52}) +var d2 = new(field.Element).Add(d, d) + +func (v *projCached) FromP3(p *Point) *projCached { + v.YplusX.Add(&p.y, &p.x) + v.YminusX.Subtract(&p.y, &p.x) + v.Z.Set(&p.z) + v.T2d.Multiply(&p.t, d2) + return v +} + +func (v *affineCached) FromP3(p *Point) *affineCached { + v.YplusX.Add(&p.y, &p.x) + v.YminusX.Subtract(&p.y, &p.x) + v.T2d.Multiply(&p.t, d2) + + var invZ field.Element + invZ.Invert(&p.z) + v.YplusX.Multiply(&v.YplusX, &invZ) + v.YminusX.Multiply(&v.YminusX, &invZ) + v.T2d.Multiply(&v.T2d, &invZ) + return v +} + +// (Re)addition and subtraction. + +// Add sets v = p + q, and returns v. +func (v *Point) Add(p, q *Point) *Point { + checkInitialized(p, q) + qCached := new(projCached).FromP3(q) + result := new(projP1xP1).Add(p, qCached) + return v.fromP1xP1(result) +} + +// Subtract sets v = p - q, and returns v. +func (v *Point) Subtract(p, q *Point) *Point { + checkInitialized(p, q) + qCached := new(projCached).FromP3(q) + result := new(projP1xP1).Sub(p, qCached) + return v.fromP1xP1(result) +} + +func (v *projP1xP1) Add(p *Point, q *projCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YplusX) + MM.Multiply(&YminusX, &q.YminusX) + TT2d.Multiply(&p.t, &q.T2d) + ZZ2.Multiply(&p.z, &q.Z) + + ZZ2.Add(&ZZ2, &ZZ2) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Add(&ZZ2, &TT2d) + v.T.Subtract(&ZZ2, &TT2d) + return v +} + +func (v *projP1xP1) Sub(p *Point, q *projCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YminusX) // flipped sign + MM.Multiply(&YminusX, &q.YplusX) // flipped sign + TT2d.Multiply(&p.t, &q.T2d) + ZZ2.Multiply(&p.z, &q.Z) + + ZZ2.Add(&ZZ2, &ZZ2) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Subtract(&ZZ2, &TT2d) // flipped sign + v.T.Add(&ZZ2, &TT2d) // flipped sign + return v +} + +func (v *projP1xP1) AddAffine(p *Point, q *affineCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YplusX) + MM.Multiply(&YminusX, &q.YminusX) + TT2d.Multiply(&p.t, &q.T2d) + + Z2.Add(&p.z, &p.z) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Add(&Z2, &TT2d) + v.T.Subtract(&Z2, &TT2d) + return v +} + +func (v *projP1xP1) SubAffine(p *Point, q *affineCached) *projP1xP1 { + var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element + + YplusX.Add(&p.y, &p.x) + YminusX.Subtract(&p.y, &p.x) + + PP.Multiply(&YplusX, &q.YminusX) // flipped sign + MM.Multiply(&YminusX, &q.YplusX) // flipped sign + TT2d.Multiply(&p.t, &q.T2d) + + Z2.Add(&p.z, &p.z) + + v.X.Subtract(&PP, &MM) + v.Y.Add(&PP, &MM) + v.Z.Subtract(&Z2, &TT2d) // flipped sign + v.T.Add(&Z2, &TT2d) // flipped sign + return v +} + +// Doubling. + +func (v *projP1xP1) Double(p *projP2) *projP1xP1 { + var XX, YY, ZZ2, XplusYsq field.Element + + XX.Square(&p.X) + YY.Square(&p.Y) + ZZ2.Square(&p.Z) + ZZ2.Add(&ZZ2, &ZZ2) + XplusYsq.Add(&p.X, &p.Y) + XplusYsq.Square(&XplusYsq) + + v.Y.Add(&YY, &XX) + v.Z.Subtract(&YY, &XX) + + v.X.Subtract(&XplusYsq, &v.Y) + v.T.Subtract(&ZZ2, &v.Z) + return v +} + +// Negation. + +// Negate sets v = -p, and returns v. +func (v *Point) Negate(p *Point) *Point { + checkInitialized(p) + v.x.Negate(&p.x) + v.y.Set(&p.y) + v.z.Set(&p.z) + v.t.Negate(&p.t) + return v +} + +// Equal returns 1 if v is equivalent to u, and 0 otherwise. +func (v *Point) Equal(u *Point) int { + checkInitialized(v, u) + + var t1, t2, t3, t4 field.Element + t1.Multiply(&v.x, &u.z) + t2.Multiply(&u.x, &v.z) + t3.Multiply(&v.y, &u.z) + t4.Multiply(&u.y, &v.z) + + return t1.Equal(&t2) & t3.Equal(&t4) +} + +// Constant-time operations + +// Select sets v to a if cond == 1 and to b if cond == 0. +func (v *projCached) Select(a, b *projCached, cond int) *projCached { + v.YplusX.Select(&a.YplusX, &b.YplusX, cond) + v.YminusX.Select(&a.YminusX, &b.YminusX, cond) + v.Z.Select(&a.Z, &b.Z, cond) + v.T2d.Select(&a.T2d, &b.T2d, cond) + return v +} + +// Select sets v to a if cond == 1 and to b if cond == 0. +func (v *affineCached) Select(a, b *affineCached, cond int) *affineCached { + v.YplusX.Select(&a.YplusX, &b.YplusX, cond) + v.YminusX.Select(&a.YminusX, &b.YminusX, cond) + v.T2d.Select(&a.T2d, &b.T2d, cond) + return v +} + +// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0. +func (v *projCached) CondNeg(cond int) *projCached { + v.YplusX.Swap(&v.YminusX, cond) + v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond) + return v +} + +// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0. +func (v *affineCached) CondNeg(cond int) *affineCached { + v.YplusX.Swap(&v.YminusX, cond) + v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond) + return v +} diff --git a/vendor/filippo.io/edwards25519/extra.go b/vendor/filippo.io/edwards25519/extra.go new file mode 100644 index 00000000..d152d68f --- /dev/null +++ b/vendor/filippo.io/edwards25519/extra.go @@ -0,0 +1,349 @@ +// Copyright (c) 2021 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 edwards25519 + +// This file contains additional functionality that is not included in the +// upstream crypto/internal/edwards25519 package. + +import ( + "errors" + + "filippo.io/edwards25519/field" +) + +// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where +// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522. +func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. Don't change the style without making + // sure it doesn't increase the inliner cost. + var e [4]field.Element + X, Y, Z, T = v.extendedCoordinates(&e) + return +} + +func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) { + checkInitialized(v) + X = e[0].Set(&v.x) + Y = e[1].Set(&v.y) + Z = e[2].Set(&v.z) + T = e[3].Set(&v.t) + return +} + +// SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where +// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522. +// +// If the coordinates are invalid or don't represent a valid point on the curve, +// SetExtendedCoordinates returns nil and an error and the receiver is +// unchanged. Otherwise, SetExtendedCoordinates returns v. +func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) { + if !isOnCurve(X, Y, Z, T) { + return nil, errors.New("edwards25519: invalid point coordinates") + } + v.x.Set(X) + v.y.Set(Y) + v.z.Set(Z) + v.t.Set(T) + return v, nil +} + +func isOnCurve(X, Y, Z, T *field.Element) bool { + var lhs, rhs field.Element + XX := new(field.Element).Square(X) + YY := new(field.Element).Square(Y) + ZZ := new(field.Element).Square(Z) + TT := new(field.Element).Square(T) + // -x² + y² = 1 + dx²y² + // -(X/Z)² + (Y/Z)² = 1 + d(T/Z)² + // -X² + Y² = Z² + dT² + lhs.Subtract(YY, XX) + rhs.Multiply(d, TT).Add(&rhs, ZZ) + if lhs.Equal(&rhs) != 1 { + return false + } + // xy = T/Z + // XY/Z² = T/Z + // XY = TZ + lhs.Multiply(X, Y) + rhs.Multiply(T, Z) + return lhs.Equal(&rhs) == 1 +} + +// BytesMontgomery converts v to a point on the birationally-equivalent +// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding +// according to RFC 7748. +// +// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode +// to the same value. If v is the identity point, BytesMontgomery returns 32 +// zero bytes, analogously to the X25519 function. +// +// The lack of an inverse operation (such as SetMontgomeryBytes) is deliberate: +// while every valid edwards25519 point has a unique u-coordinate Montgomery +// encoding, X25519 accepts inputs on the quadratic twist, which don't correspond +// to any edwards25519 point, and every other X25519 input corresponds to two +// edwards25519 points. +func (v *Point) BytesMontgomery() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var buf [32]byte + return v.bytesMontgomery(&buf) +} + +func (v *Point) bytesMontgomery(buf *[32]byte) []byte { + checkInitialized(v) + + // RFC 7748, Section 4.1 provides the bilinear map to calculate the + // Montgomery u-coordinate + // + // u = (1 + y) / (1 - y) + // + // where y = Y / Z. + + var y, recip, u field.Element + + y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z + recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y) + u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r + + return copyFieldElement(buf, &u) +} + +// MultByCofactor sets v = 8 * p, and returns v. +func (v *Point) MultByCofactor(p *Point) *Point { + checkInitialized(p) + result := projP1xP1{} + pp := (&projP2{}).FromP3(p) + result.Double(pp) + pp.FromP1xP1(&result) + result.Double(pp) + pp.FromP1xP1(&result) + result.Double(pp) + return v.fromP1xP1(&result) +} + +// Given k > 0, set s = s**(2*i). +func (s *Scalar) pow2k(k int) { + for i := 0; i < k; i++ { + s.Multiply(s, s) + } +} + +// Invert sets s to the inverse of a nonzero scalar v, and returns s. +// +// If t is zero, Invert returns zero. +func (s *Scalar) Invert(t *Scalar) *Scalar { + // Uses a hardcoded sliding window of width 4. + var table [8]Scalar + var tt Scalar + tt.Multiply(t, t) + table[0] = *t + for i := 0; i < 7; i++ { + table[i+1].Multiply(&table[i], &tt) + } + // Now table = [t**1, t**3, t**5, t**7, t**9, t**11, t**13, t**15] + // so t**k = t[k/2] for odd k + + // To compute the sliding window digits, use the following Sage script: + + // sage: import itertools + // sage: def sliding_window(w,k): + // ....: digits = [] + // ....: while k > 0: + // ....: if k % 2 == 1: + // ....: kmod = k % (2**w) + // ....: digits.append(kmod) + // ....: k = k - kmod + // ....: else: + // ....: digits.append(0) + // ....: k = k // 2 + // ....: return digits + + // Now we can compute s roughly as follows: + + // sage: s = 1 + // sage: for coeff in reversed(sliding_window(4,l-2)): + // ....: s = s*s + // ....: if coeff > 0 : + // ....: s = s*t**coeff + + // This works on one bit at a time, with many runs of zeros. + // The digits can be collapsed into [(count, coeff)] as follows: + + // sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))] + + // Entries of the form (k, 0) turn into pow2k(k) + // Entries of the form (1, coeff) turn into a squaring and then a table lookup. + // We can fold the squaring into the previous pow2k(k) as pow2k(k+1). + + *s = table[1/2] + s.pow2k(127 + 1) + s.Multiply(s, &table[1/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[11/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[13/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[5/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[1/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[11/2]) + s.pow2k(5 + 1) + s.Multiply(s, &table[11/2]) + s.pow2k(9 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[3/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[13/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[7/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[9/2]) + s.pow2k(3 + 1) + s.Multiply(s, &table[15/2]) + s.pow2k(4 + 1) + s.Multiply(s, &table[11/2]) + + return s +} + +// MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v. +// +// Execution time depends only on the lengths of the two slices, which must match. +func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point { + if len(scalars) != len(points) { + panic("edwards25519: called MultiScalarMult with different size inputs") + } + checkInitialized(points...) + + // Proceed as in the single-base case, but share doublings + // between each point in the multiscalar equation. + + // Build lookup tables for each point + tables := make([]projLookupTable, len(points)) + for i := range tables { + tables[i].FromP3(points[i]) + } + // Compute signed radix-16 digits for each scalar + digits := make([][64]int8, len(scalars)) + for i := range digits { + digits[i] = scalars[i].signedRadix16() + } + + // Unwrap first loop iteration to save computing 16*identity + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + // Lookup-and-add the appropriate multiple of each input point + for j := range tables { + tables[j].SelectInto(multiple, digits[j][63]) + tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords + v.fromP1xP1(tmp1) // update v + } + tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration + for i := 62; i >= 0; i-- { + tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords + v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords + // Lookup-and-add the appropriate multiple of each input point + for j := range tables { + tables[j].SelectInto(multiple, digits[j][i]) + tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords + v.fromP1xP1(tmp1) // update v + } + tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration + } + return v +} + +// VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v. +// +// Execution time depends on the inputs. +func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point { + if len(scalars) != len(points) { + panic("edwards25519: called VarTimeMultiScalarMult with different size inputs") + } + checkInitialized(points...) + + // Generalize double-base NAF computation to arbitrary sizes. + // Here all the points are dynamic, so we only use the smaller + // tables. + + // Build lookup tables for each point + tables := make([]nafLookupTable5, len(points)) + for i := range tables { + tables[i].FromP3(points[i]) + } + // Compute a NAF for each scalar + nafs := make([][256]int8, len(scalars)) + for i := range nafs { + nafs[i] = scalars[i].nonAdjacentForm(5) + } + + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + tmp2.Zero() + + // Move from high to low bits, doubling the accumulator + // at each iteration and checking whether there is a nonzero + // coefficient to look up a multiple of. + // + // Skip trying to find the first nonzero coefficent, because + // searching might be more work than a few extra doublings. + for i := 255; i >= 0; i-- { + tmp1.Double(tmp2) + + for j := range nafs { + if nafs[j][i] > 0 { + v.fromP1xP1(tmp1) + tables[j].SelectInto(multiple, nafs[j][i]) + tmp1.Add(v, multiple) + } else if nafs[j][i] < 0 { + v.fromP1xP1(tmp1) + tables[j].SelectInto(multiple, -nafs[j][i]) + tmp1.Sub(v, multiple) + } + } + + tmp2.FromP1xP1(tmp1) + } + + v.fromP2(tmp2) + return v +} diff --git a/vendor/filippo.io/edwards25519/field/fe.go b/vendor/filippo.io/edwards25519/field/fe.go new file mode 100644 index 00000000..5518ef2b --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe.go @@ -0,0 +1,420 @@ +// Copyright (c) 2017 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 field implements fast arithmetic modulo 2^255-19. +package field + +import ( + "crypto/subtle" + "encoding/binary" + "errors" + "math/bits" +) + +// Element represents an element of the field GF(2^255-19). Note that this +// is not a cryptographically secure group, and should only be used to interact +// with edwards25519.Point coordinates. +// +// This type works similarly to math/big.Int, and all arguments and receivers +// are allowed to alias. +// +// The zero value is a valid zero element. +type Element struct { + // An element t represents the integer + // t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204 + // + // Between operations, all limbs are expected to be lower than 2^52. + l0 uint64 + l1 uint64 + l2 uint64 + l3 uint64 + l4 uint64 +} + +const maskLow51Bits uint64 = (1 << 51) - 1 + +var feZero = &Element{0, 0, 0, 0, 0} + +// Zero sets v = 0, and returns v. +func (v *Element) Zero() *Element { + *v = *feZero + return v +} + +var feOne = &Element{1, 0, 0, 0, 0} + +// One sets v = 1, and returns v. +func (v *Element) One() *Element { + *v = *feOne + return v +} + +// reduce reduces v modulo 2^255 - 19 and returns it. +func (v *Element) reduce() *Element { + v.carryPropagate() + + // After the light reduction we now have a field element representation + // v < 2^255 + 2^13 * 19, but need v < 2^255 - 19. + + // If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1, + // generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise. + c := (v.l0 + 19) >> 51 + c = (v.l1 + c) >> 51 + c = (v.l2 + c) >> 51 + c = (v.l3 + c) >> 51 + c = (v.l4 + c) >> 51 + + // If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's + // effectively applying the reduction identity to the carry. + v.l0 += 19 * c + + v.l1 += v.l0 >> 51 + v.l0 = v.l0 & maskLow51Bits + v.l2 += v.l1 >> 51 + v.l1 = v.l1 & maskLow51Bits + v.l3 += v.l2 >> 51 + v.l2 = v.l2 & maskLow51Bits + v.l4 += v.l3 >> 51 + v.l3 = v.l3 & maskLow51Bits + // no additional carry + v.l4 = v.l4 & maskLow51Bits + + return v +} + +// Add sets v = a + b, and returns v. +func (v *Element) Add(a, b *Element) *Element { + v.l0 = a.l0 + b.l0 + v.l1 = a.l1 + b.l1 + v.l2 = a.l2 + b.l2 + v.l3 = a.l3 + b.l3 + v.l4 = a.l4 + b.l4 + // Using the generic implementation here is actually faster than the + // assembly. Probably because the body of this function is so simple that + // the compiler can figure out better optimizations by inlining the carry + // propagation. + return v.carryPropagateGeneric() +} + +// Subtract sets v = a - b, and returns v. +func (v *Element) Subtract(a, b *Element) *Element { + // We first add 2 * p, to guarantee the subtraction won't underflow, and + // then subtract b (which can be up to 2^255 + 2^13 * 19). + v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0 + v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1 + v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2 + v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3 + v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4 + return v.carryPropagate() +} + +// Negate sets v = -a, and returns v. +func (v *Element) Negate(a *Element) *Element { + return v.Subtract(feZero, a) +} + +// Invert sets v = 1/z mod p, and returns v. +// +// If z == 0, Invert returns v = 0. +func (v *Element) Invert(z *Element) *Element { + // Inversion is implemented as exponentiation with exponent p − 2. It uses the + // same sequence of 255 squarings and 11 multiplications as [Curve25519]. + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element + + z2.Square(z) // 2 + t.Square(&z2) // 4 + t.Square(&t) // 8 + z9.Multiply(&t, z) // 9 + z11.Multiply(&z9, &z2) // 11 + t.Square(&z11) // 22 + z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 + + t.Square(&z2_5_0) // 2^6 - 2^1 + for i := 0; i < 4; i++ { + t.Square(&t) // 2^10 - 2^5 + } + z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 + + t.Square(&z2_10_0) // 2^11 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^20 - 2^10 + } + z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 + + t.Square(&z2_20_0) // 2^21 - 2^1 + for i := 0; i < 19; i++ { + t.Square(&t) // 2^40 - 2^20 + } + t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 + + t.Square(&t) // 2^41 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^50 - 2^10 + } + z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 + + t.Square(&z2_50_0) // 2^51 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^100 - 2^50 + } + z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 + + t.Square(&z2_100_0) // 2^101 - 2^1 + for i := 0; i < 99; i++ { + t.Square(&t) // 2^200 - 2^100 + } + t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 + + t.Square(&t) // 2^201 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^250 - 2^50 + } + t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 + + t.Square(&t) // 2^251 - 2^1 + t.Square(&t) // 2^252 - 2^2 + t.Square(&t) // 2^253 - 2^3 + t.Square(&t) // 2^254 - 2^4 + t.Square(&t) // 2^255 - 2^5 + + return v.Multiply(&t, &z11) // 2^255 - 21 +} + +// Set sets v = a, and returns v. +func (v *Element) Set(a *Element) *Element { + *v = *a + return v +} + +// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is +// not of the right length, SetBytes returns nil and an error, and the +// receiver is unchanged. +// +// Consistent with RFC 7748, the most significant bit (the high bit of the +// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1) +// are accepted. Note that this is laxer than specified by RFC 8032, but +// consistent with most Ed25519 implementations. +func (v *Element) SetBytes(x []byte) (*Element, error) { + if len(x) != 32 { + return nil, errors.New("edwards25519: invalid field element input size") + } + + // Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51). + v.l0 = binary.LittleEndian.Uint64(x[0:8]) + v.l0 &= maskLow51Bits + // Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51). + v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3 + v.l1 &= maskLow51Bits + // Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51). + v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6 + v.l2 &= maskLow51Bits + // Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51). + v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1 + v.l3 &= maskLow51Bits + // Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51). + // Note: not bytes 25:33, shift 4, to avoid overread. + v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12 + v.l4 &= maskLow51Bits + + return v, nil +} + +// Bytes returns the canonical 32-byte little-endian encoding of v. +func (v *Element) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var out [32]byte + return v.bytes(&out) +} + +func (v *Element) bytes(out *[32]byte) []byte { + t := *v + t.reduce() + + var buf [8]byte + for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} { + bitsOffset := i * 51 + binary.LittleEndian.PutUint64(buf[:], l<= len(out) { + break + } + out[off] |= bb + } + } + + return out[:] +} + +// Equal returns 1 if v and u are equal, and 0 otherwise. +func (v *Element) Equal(u *Element) int { + sa, sv := u.Bytes(), v.Bytes() + return subtle.ConstantTimeCompare(sa, sv) +} + +// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise. +func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) } + +// Select sets v to a if cond == 1, and to b if cond == 0. +func (v *Element) Select(a, b *Element, cond int) *Element { + m := mask64Bits(cond) + v.l0 = (m & a.l0) | (^m & b.l0) + v.l1 = (m & a.l1) | (^m & b.l1) + v.l2 = (m & a.l2) | (^m & b.l2) + v.l3 = (m & a.l3) | (^m & b.l3) + v.l4 = (m & a.l4) | (^m & b.l4) + return v +} + +// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v. +func (v *Element) Swap(u *Element, cond int) { + m := mask64Bits(cond) + t := m & (v.l0 ^ u.l0) + v.l0 ^= t + u.l0 ^= t + t = m & (v.l1 ^ u.l1) + v.l1 ^= t + u.l1 ^= t + t = m & (v.l2 ^ u.l2) + v.l2 ^= t + u.l2 ^= t + t = m & (v.l3 ^ u.l3) + v.l3 ^= t + u.l3 ^= t + t = m & (v.l4 ^ u.l4) + v.l4 ^= t + u.l4 ^= t +} + +// IsNegative returns 1 if v is negative, and 0 otherwise. +func (v *Element) IsNegative() int { + return int(v.Bytes()[0] & 1) +} + +// Absolute sets v to |u|, and returns v. +func (v *Element) Absolute(u *Element) *Element { + return v.Select(new(Element).Negate(u), u, u.IsNegative()) +} + +// Multiply sets v = x * y, and returns v. +func (v *Element) Multiply(x, y *Element) *Element { + feMul(v, x, y) + return v +} + +// Square sets v = x * x, and returns v. +func (v *Element) Square(x *Element) *Element { + feSquare(v, x) + return v +} + +// Mult32 sets v = x * y, and returns v. +func (v *Element) Mult32(x *Element, y uint32) *Element { + x0lo, x0hi := mul51(x.l0, y) + x1lo, x1hi := mul51(x.l1, y) + x2lo, x2hi := mul51(x.l2, y) + x3lo, x3hi := mul51(x.l3, y) + x4lo, x4hi := mul51(x.l4, y) + v.l0 = x0lo + 19*x4hi // carried over per the reduction identity + v.l1 = x1lo + x0hi + v.l2 = x2lo + x1hi + v.l3 = x3lo + x2hi + v.l4 = x4lo + x3hi + // The hi portions are going to be only 32 bits, plus any previous excess, + // so we can skip the carry propagation. + return v +} + +// mul51 returns lo + hi * 2⁵¹ = a * b. +func mul51(a uint64, b uint32) (lo uint64, hi uint64) { + mh, ml := bits.Mul64(a, uint64(b)) + lo = ml & maskLow51Bits + hi = (mh << 13) | (ml >> 51) + return +} + +// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3. +func (v *Element) Pow22523(x *Element) *Element { + var t0, t1, t2 Element + + t0.Square(x) // x^2 + t1.Square(&t0) // x^4 + t1.Square(&t1) // x^8 + t1.Multiply(x, &t1) // x^9 + t0.Multiply(&t0, &t1) // x^11 + t0.Square(&t0) // x^22 + t0.Multiply(&t1, &t0) // x^31 + t1.Square(&t0) // x^62 + for i := 1; i < 5; i++ { // x^992 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1 + t1.Square(&t0) // 2^11 - 2 + for i := 1; i < 10; i++ { // 2^20 - 2^10 + t1.Square(&t1) + } + t1.Multiply(&t1, &t0) // 2^20 - 1 + t2.Square(&t1) // 2^21 - 2 + for i := 1; i < 20; i++ { // 2^40 - 2^20 + t2.Square(&t2) + } + t1.Multiply(&t2, &t1) // 2^40 - 1 + t1.Square(&t1) // 2^41 - 2 + for i := 1; i < 10; i++ { // 2^50 - 2^10 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // 2^50 - 1 + t1.Square(&t0) // 2^51 - 2 + for i := 1; i < 50; i++ { // 2^100 - 2^50 + t1.Square(&t1) + } + t1.Multiply(&t1, &t0) // 2^100 - 1 + t2.Square(&t1) // 2^101 - 2 + for i := 1; i < 100; i++ { // 2^200 - 2^100 + t2.Square(&t2) + } + t1.Multiply(&t2, &t1) // 2^200 - 1 + t1.Square(&t1) // 2^201 - 2 + for i := 1; i < 50; i++ { // 2^250 - 2^50 + t1.Square(&t1) + } + t0.Multiply(&t1, &t0) // 2^250 - 1 + t0.Square(&t0) // 2^251 - 2 + t0.Square(&t0) // 2^252 - 4 + return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3) +} + +// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion. +var sqrtM1 = &Element{1718705420411056, 234908883556509, + 2233514472574048, 2117202627021982, 765476049583133} + +// SqrtRatio sets r to the non-negative square root of the ratio of u and v. +// +// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio +// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00, +// and returns r and 0. +func (r *Element) SqrtRatio(u, v *Element) (R *Element, wasSquare int) { + t0 := new(Element) + + // r = (u * v3) * (u * v7)^((p-5)/8) + v2 := new(Element).Square(v) + uv3 := new(Element).Multiply(u, t0.Multiply(v2, v)) + uv7 := new(Element).Multiply(uv3, t0.Square(v2)) + rr := new(Element).Multiply(uv3, t0.Pow22523(uv7)) + + check := new(Element).Multiply(v, t0.Square(rr)) // check = v * r^2 + + uNeg := new(Element).Negate(u) + correctSignSqrt := check.Equal(u) + flippedSignSqrt := check.Equal(uNeg) + flippedSignSqrtI := check.Equal(t0.Multiply(uNeg, sqrtM1)) + + rPrime := new(Element).Multiply(rr, sqrtM1) // r_prime = SQRT_M1 * r + // r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r) + rr.Select(rPrime, rr, flippedSignSqrt|flippedSignSqrtI) + + r.Absolute(rr) // Choose the nonnegative square root. + return r, correctSignSqrt | flippedSignSqrt +} diff --git a/vendor/filippo.io/edwards25519/field/fe_amd64.go b/vendor/filippo.io/edwards25519/field/fe_amd64.go new file mode 100644 index 00000000..edcf163c --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_amd64.go @@ -0,0 +1,16 @@ +// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. + +//go:build amd64 && gc && !purego +// +build amd64,gc,!purego + +package field + +// feMul sets out = a * b. It works like feMulGeneric. +// +//go:noescape +func feMul(out *Element, a *Element, b *Element) + +// feSquare sets out = a * a. It works like feSquareGeneric. +// +//go:noescape +func feSquare(out *Element, a *Element) diff --git a/vendor/filippo.io/edwards25519/field/fe_amd64.s b/vendor/filippo.io/edwards25519/field/fe_amd64.s new file mode 100644 index 00000000..293f013c --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_amd64.s @@ -0,0 +1,379 @@ +// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. + +//go:build amd64 && gc && !purego +// +build amd64,gc,!purego + +#include "textflag.h" + +// func feMul(out *Element, a *Element, b *Element) +TEXT ·feMul(SB), NOSPLIT, $0-24 + MOVQ a+8(FP), CX + MOVQ b+16(FP), BX + + // r0 = a0×b0 + MOVQ (CX), AX + MULQ (BX) + MOVQ AX, DI + MOVQ DX, SI + + // r0 += 19×a1×b4 + MOVQ 8(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a2×b3 + MOVQ 16(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a3×b2 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 16(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r0 += 19×a4×b1 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 8(BX) + ADDQ AX, DI + ADCQ DX, SI + + // r1 = a0×b1 + MOVQ (CX), AX + MULQ 8(BX) + MOVQ AX, R9 + MOVQ DX, R8 + + // r1 += a1×b0 + MOVQ 8(CX), AX + MULQ (BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a2×b4 + MOVQ 16(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a3×b3 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r1 += 19×a4×b2 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 16(BX) + ADDQ AX, R9 + ADCQ DX, R8 + + // r2 = a0×b2 + MOVQ (CX), AX + MULQ 16(BX) + MOVQ AX, R11 + MOVQ DX, R10 + + // r2 += a1×b1 + MOVQ 8(CX), AX + MULQ 8(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += a2×b0 + MOVQ 16(CX), AX + MULQ (BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += 19×a3×b4 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r2 += 19×a4×b3 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(BX) + ADDQ AX, R11 + ADCQ DX, R10 + + // r3 = a0×b3 + MOVQ (CX), AX + MULQ 24(BX) + MOVQ AX, R13 + MOVQ DX, R12 + + // r3 += a1×b2 + MOVQ 8(CX), AX + MULQ 16(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += a2×b1 + MOVQ 16(CX), AX + MULQ 8(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += a3×b0 + MOVQ 24(CX), AX + MULQ (BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r3 += 19×a4×b4 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(BX) + ADDQ AX, R13 + ADCQ DX, R12 + + // r4 = a0×b4 + MOVQ (CX), AX + MULQ 32(BX) + MOVQ AX, R15 + MOVQ DX, R14 + + // r4 += a1×b3 + MOVQ 8(CX), AX + MULQ 24(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a2×b2 + MOVQ 16(CX), AX + MULQ 16(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a3×b1 + MOVQ 24(CX), AX + MULQ 8(BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // r4 += a4×b0 + MOVQ 32(CX), AX + MULQ (BX) + ADDQ AX, R15 + ADCQ DX, R14 + + // First reduction chain + MOVQ $0x0007ffffffffffff, AX + SHLQ $0x0d, DI, SI + SHLQ $0x0d, R9, R8 + SHLQ $0x0d, R11, R10 + SHLQ $0x0d, R13, R12 + SHLQ $0x0d, R15, R14 + ANDQ AX, DI + IMUL3Q $0x13, R14, R14 + ADDQ R14, DI + ANDQ AX, R9 + ADDQ SI, R9 + ANDQ AX, R11 + ADDQ R8, R11 + ANDQ AX, R13 + ADDQ R10, R13 + ANDQ AX, R15 + ADDQ R12, R15 + + // Second reduction chain (carryPropagate) + MOVQ DI, SI + SHRQ $0x33, SI + MOVQ R9, R8 + SHRQ $0x33, R8 + MOVQ R11, R10 + SHRQ $0x33, R10 + MOVQ R13, R12 + SHRQ $0x33, R12 + MOVQ R15, R14 + SHRQ $0x33, R14 + ANDQ AX, DI + IMUL3Q $0x13, R14, R14 + ADDQ R14, DI + ANDQ AX, R9 + ADDQ SI, R9 + ANDQ AX, R11 + ADDQ R8, R11 + ANDQ AX, R13 + ADDQ R10, R13 + ANDQ AX, R15 + ADDQ R12, R15 + + // Store output + MOVQ out+0(FP), AX + MOVQ DI, (AX) + MOVQ R9, 8(AX) + MOVQ R11, 16(AX) + MOVQ R13, 24(AX) + MOVQ R15, 32(AX) + RET + +// func feSquare(out *Element, a *Element) +TEXT ·feSquare(SB), NOSPLIT, $0-16 + MOVQ a+8(FP), CX + + // r0 = l0×l0 + MOVQ (CX), AX + MULQ (CX) + MOVQ AX, SI + MOVQ DX, BX + + // r0 += 38×l1×l4 + MOVQ 8(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, SI + ADCQ DX, BX + + // r0 += 38×l2×l3 + MOVQ 16(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 24(CX) + ADDQ AX, SI + ADCQ DX, BX + + // r1 = 2×l0×l1 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 8(CX) + MOVQ AX, R8 + MOVQ DX, DI + + // r1 += 38×l2×l4 + MOVQ 16(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, R8 + ADCQ DX, DI + + // r1 += 19×l3×l3 + MOVQ 24(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 24(CX) + ADDQ AX, R8 + ADCQ DX, DI + + // r2 = 2×l0×l2 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 16(CX) + MOVQ AX, R10 + MOVQ DX, R9 + + // r2 += l1×l1 + MOVQ 8(CX), AX + MULQ 8(CX) + ADDQ AX, R10 + ADCQ DX, R9 + + // r2 += 38×l3×l4 + MOVQ 24(CX), AX + IMUL3Q $0x26, AX, AX + MULQ 32(CX) + ADDQ AX, R10 + ADCQ DX, R9 + + // r3 = 2×l0×l3 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 24(CX) + MOVQ AX, R12 + MOVQ DX, R11 + + // r3 += 2×l1×l2 + MOVQ 8(CX), AX + IMUL3Q $0x02, AX, AX + MULQ 16(CX) + ADDQ AX, R12 + ADCQ DX, R11 + + // r3 += 19×l4×l4 + MOVQ 32(CX), AX + IMUL3Q $0x13, AX, AX + MULQ 32(CX) + ADDQ AX, R12 + ADCQ DX, R11 + + // r4 = 2×l0×l4 + MOVQ (CX), AX + SHLQ $0x01, AX + MULQ 32(CX) + MOVQ AX, R14 + MOVQ DX, R13 + + // r4 += 2×l1×l3 + MOVQ 8(CX), AX + IMUL3Q $0x02, AX, AX + MULQ 24(CX) + ADDQ AX, R14 + ADCQ DX, R13 + + // r4 += l2×l2 + MOVQ 16(CX), AX + MULQ 16(CX) + ADDQ AX, R14 + ADCQ DX, R13 + + // First reduction chain + MOVQ $0x0007ffffffffffff, AX + SHLQ $0x0d, SI, BX + SHLQ $0x0d, R8, DI + SHLQ $0x0d, R10, R9 + SHLQ $0x0d, R12, R11 + SHLQ $0x0d, R14, R13 + ANDQ AX, SI + IMUL3Q $0x13, R13, R13 + ADDQ R13, SI + ANDQ AX, R8 + ADDQ BX, R8 + ANDQ AX, R10 + ADDQ DI, R10 + ANDQ AX, R12 + ADDQ R9, R12 + ANDQ AX, R14 + ADDQ R11, R14 + + // Second reduction chain (carryPropagate) + MOVQ SI, BX + SHRQ $0x33, BX + MOVQ R8, DI + SHRQ $0x33, DI + MOVQ R10, R9 + SHRQ $0x33, R9 + MOVQ R12, R11 + SHRQ $0x33, R11 + MOVQ R14, R13 + SHRQ $0x33, R13 + ANDQ AX, SI + IMUL3Q $0x13, R13, R13 + ADDQ R13, SI + ANDQ AX, R8 + ADDQ BX, R8 + ANDQ AX, R10 + ADDQ DI, R10 + ANDQ AX, R12 + ADDQ R9, R12 + ANDQ AX, R14 + ADDQ R11, R14 + + // Store output + MOVQ out+0(FP), AX + MOVQ SI, (AX) + MOVQ R8, 8(AX) + MOVQ R10, 16(AX) + MOVQ R12, 24(AX) + MOVQ R14, 32(AX) + RET diff --git a/vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go b/vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go new file mode 100644 index 00000000..ddb6c9b8 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go @@ -0,0 +1,12 @@ +// Copyright (c) 2019 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. + +//go:build !amd64 || !gc || purego +// +build !amd64 !gc purego + +package field + +func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } + +func feSquare(v, x *Element) { feSquareGeneric(v, x) } diff --git a/vendor/filippo.io/edwards25519/field/fe_arm64.go b/vendor/filippo.io/edwards25519/field/fe_arm64.go new file mode 100644 index 00000000..af459ef5 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_arm64.go @@ -0,0 +1,16 @@ +// Copyright (c) 2020 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. + +//go:build arm64 && gc && !purego +// +build arm64,gc,!purego + +package field + +//go:noescape +func carryPropagate(v *Element) + +func (v *Element) carryPropagate() *Element { + carryPropagate(v) + return v +} diff --git a/vendor/filippo.io/edwards25519/field/fe_arm64.s b/vendor/filippo.io/edwards25519/field/fe_arm64.s new file mode 100644 index 00000000..3126a434 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_arm64.s @@ -0,0 +1,42 @@ +// Copyright (c) 2020 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. + +//go:build arm64 && gc && !purego + +#include "textflag.h" + +// carryPropagate works exactly like carryPropagateGeneric and uses the +// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but +// avoids loading R0-R4 twice and uses LDP and STP. +// +// See https://golang.org/issues/43145 for the main compiler issue. +// +// func carryPropagate(v *Element) +TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8 + MOVD v+0(FP), R20 + + LDP 0(R20), (R0, R1) + LDP 16(R20), (R2, R3) + MOVD 32(R20), R4 + + AND $0x7ffffffffffff, R0, R10 + AND $0x7ffffffffffff, R1, R11 + AND $0x7ffffffffffff, R2, R12 + AND $0x7ffffffffffff, R3, R13 + AND $0x7ffffffffffff, R4, R14 + + ADD R0>>51, R11, R11 + ADD R1>>51, R12, R12 + ADD R2>>51, R13, R13 + ADD R3>>51, R14, R14 + // R4>>51 * 19 + R10 -> R10 + LSR $51, R4, R21 + MOVD $19, R22 + MADD R22, R10, R21, R10 + + STP (R10, R11), 0(R20) + STP (R12, R13), 16(R20) + MOVD R14, 32(R20) + + RET diff --git a/vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go b/vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go new file mode 100644 index 00000000..234a5b2e --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go @@ -0,0 +1,12 @@ +// Copyright (c) 2021 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. + +//go:build !arm64 || !gc || purego +// +build !arm64 !gc purego + +package field + +func (v *Element) carryPropagate() *Element { + return v.carryPropagateGeneric() +} diff --git a/vendor/filippo.io/edwards25519/field/fe_extra.go b/vendor/filippo.io/edwards25519/field/fe_extra.go new file mode 100644 index 00000000..1ef503b9 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_extra.go @@ -0,0 +1,50 @@ +// Copyright (c) 2021 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 field + +import "errors" + +// This file contains additional functionality that is not included in the +// upstream crypto/ed25519/edwards25519/field package. + +// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which +// is reduced modulo the field order. If x is not of the right length, +// SetWideBytes returns nil and an error, and the receiver is unchanged. +// +// SetWideBytes is not necessary to select a uniformly distributed value, and is +// only provided for compatibility: SetBytes can be used instead as the chance +// of bias is less than 2⁻²⁵⁰. +func (v *Element) SetWideBytes(x []byte) (*Element, error) { + if len(x) != 64 { + return nil, errors.New("edwards25519: invalid SetWideBytes input size") + } + + // Split the 64 bytes into two elements, and extract the most significant + // bit of each, which is ignored by SetBytes. + lo, _ := new(Element).SetBytes(x[:32]) + loMSB := uint64(x[31] >> 7) + hi, _ := new(Element).SetBytes(x[32:]) + hiMSB := uint64(x[63] >> 7) + + // The output we want is + // + // v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹ + // + // which applying the reduction identity comes out to + // + // v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19² + // + // l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value + // (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value + // (hiMSB * 2 * 19²), so it fits in a uint64. + + v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19 + v.l1 = lo.l1 + hi.l1*2*19 + v.l2 = lo.l2 + hi.l2*2*19 + v.l3 = lo.l3 + hi.l3*2*19 + v.l4 = lo.l4 + hi.l4*2*19 + + return v.carryPropagate(), nil +} diff --git a/vendor/filippo.io/edwards25519/field/fe_generic.go b/vendor/filippo.io/edwards25519/field/fe_generic.go new file mode 100644 index 00000000..86f5fd95 --- /dev/null +++ b/vendor/filippo.io/edwards25519/field/fe_generic.go @@ -0,0 +1,266 @@ +// Copyright (c) 2017 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 field + +import "math/bits" + +// uint128 holds a 128-bit number as two 64-bit limbs, for use with the +// bits.Mul64 and bits.Add64 intrinsics. +type uint128 struct { + lo, hi uint64 +} + +// mul64 returns a * b. +func mul64(a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + return uint128{lo, hi} +} + +// addMul64 returns v + a * b. +func addMul64(v uint128, a, b uint64) uint128 { + hi, lo := bits.Mul64(a, b) + lo, c := bits.Add64(lo, v.lo, 0) + hi, _ = bits.Add64(hi, v.hi, c) + return uint128{lo, hi} +} + +// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits. +func shiftRightBy51(a uint128) uint64 { + return (a.hi << (64 - 51)) | (a.lo >> 51) +} + +func feMulGeneric(v, a, b *Element) { + a0 := a.l0 + a1 := a.l1 + a2 := a.l2 + a3 := a.l3 + a4 := a.l4 + + b0 := b.l0 + b1 := b.l1 + b2 := b.l2 + b3 := b.l3 + b4 := b.l4 + + // Limb multiplication works like pen-and-paper columnar multiplication, but + // with 51-bit limbs instead of digits. + // + // a4 a3 a2 a1 a0 x + // b4 b3 b2 b1 b0 = + // ------------------------ + // a4b0 a3b0 a2b0 a1b0 a0b0 + + // a4b1 a3b1 a2b1 a1b1 a0b1 + + // a4b2 a3b2 a2b2 a1b2 a0b2 + + // a4b3 a3b3 a2b3 a1b3 a0b3 + + // a4b4 a3b4 a2b4 a1b4 a0b4 = + // ---------------------------------------------- + // r8 r7 r6 r5 r4 r3 r2 r1 r0 + // + // We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to + // reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5, + // r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc. + // + // Reduction can be carried out simultaneously to multiplication. For + // example, we do not compute r5: whenever the result of a multiplication + // belongs to r5, like a1b4, we multiply it by 19 and add the result to r0. + // + // a4b0 a3b0 a2b0 a1b0 a0b0 + + // a3b1 a2b1 a1b1 a0b1 19×a4b1 + + // a2b2 a1b2 a0b2 19×a4b2 19×a3b2 + + // a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 + + // a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 = + // -------------------------------------- + // r4 r3 r2 r1 r0 + // + // Finally we add up the columns into wide, overlapping limbs. + + a1_19 := a1 * 19 + a2_19 := a2 * 19 + a3_19 := a3 * 19 + a4_19 := a4 * 19 + + // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) + r0 := mul64(a0, b0) + r0 = addMul64(r0, a1_19, b4) + r0 = addMul64(r0, a2_19, b3) + r0 = addMul64(r0, a3_19, b2) + r0 = addMul64(r0, a4_19, b1) + + // r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2) + r1 := mul64(a0, b1) + r1 = addMul64(r1, a1, b0) + r1 = addMul64(r1, a2_19, b4) + r1 = addMul64(r1, a3_19, b3) + r1 = addMul64(r1, a4_19, b2) + + // r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3) + r2 := mul64(a0, b2) + r2 = addMul64(r2, a1, b1) + r2 = addMul64(r2, a2, b0) + r2 = addMul64(r2, a3_19, b4) + r2 = addMul64(r2, a4_19, b3) + + // r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4 + r3 := mul64(a0, b3) + r3 = addMul64(r3, a1, b2) + r3 = addMul64(r3, a2, b1) + r3 = addMul64(r3, a3, b0) + r3 = addMul64(r3, a4_19, b4) + + // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 + r4 := mul64(a0, b4) + r4 = addMul64(r4, a1, b3) + r4 = addMul64(r4, a2, b2) + r4 = addMul64(r4, a3, b1) + r4 = addMul64(r4, a4, b0) + + // After the multiplication, we need to reduce (carry) the five coefficients + // to obtain a result with limbs that are at most slightly larger than 2⁵¹, + // to respect the Element invariant. + // + // Overall, the reduction works the same as carryPropagate, except with + // wider inputs: we take the carry for each coefficient by shifting it right + // by 51, and add it to the limb above it. The top carry is multiplied by 19 + // according to the reduction identity and added to the lowest limb. + // + // The largest coefficient (r0) will be at most 111 bits, which guarantees + // that all carries are at most 111 - 51 = 60 bits, which fits in a uint64. + // + // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) + // r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²) + // r0 < (1 + 19 × 4) × 2⁵² × 2⁵² + // r0 < 2⁷ × 2⁵² × 2⁵² + // r0 < 2¹¹¹ + // + // Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most + // 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and + // allows us to easily apply the reduction identity. + // + // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 + // r4 < 5 × 2⁵² × 2⁵² + // r4 < 2¹⁰⁷ + // + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + c4*19 + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + // Now all coefficients fit into 64-bit registers but are still too large to + // be passed around as an Element. We therefore do one last carry chain, + // where the carries will be small enough to fit in the wiggle room above 2⁵¹. + *v = Element{rr0, rr1, rr2, rr3, rr4} + v.carryPropagate() +} + +func feSquareGeneric(v, a *Element) { + l0 := a.l0 + l1 := a.l1 + l2 := a.l2 + l3 := a.l3 + l4 := a.l4 + + // Squaring works precisely like multiplication above, but thanks to its + // symmetry we get to group a few terms together. + // + // l4 l3 l2 l1 l0 x + // l4 l3 l2 l1 l0 = + // ------------------------ + // l4l0 l3l0 l2l0 l1l0 l0l0 + + // l4l1 l3l1 l2l1 l1l1 l0l1 + + // l4l2 l3l2 l2l2 l1l2 l0l2 + + // l4l3 l3l3 l2l3 l1l3 l0l3 + + // l4l4 l3l4 l2l4 l1l4 l0l4 = + // ---------------------------------------------- + // r8 r7 r6 r5 r4 r3 r2 r1 r0 + // + // l4l0 l3l0 l2l0 l1l0 l0l0 + + // l3l1 l2l1 l1l1 l0l1 19×l4l1 + + // l2l2 l1l2 l0l2 19×l4l2 19×l3l2 + + // l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 + + // l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 = + // -------------------------------------- + // r4 r3 r2 r1 r0 + // + // With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with + // only three Mul64 and four Add64, instead of five and eight. + + l0_2 := l0 * 2 + l1_2 := l1 * 2 + + l1_38 := l1 * 38 + l2_38 := l2 * 38 + l3_38 := l3 * 38 + + l3_19 := l3 * 19 + l4_19 := l4 * 19 + + // r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3) + r0 := mul64(l0, l0) + r0 = addMul64(r0, l1_38, l4) + r0 = addMul64(r0, l2_38, l3) + + // r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3 + r1 := mul64(l0_2, l1) + r1 = addMul64(r1, l2_38, l4) + r1 = addMul64(r1, l3_19, l3) + + // r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4 + r2 := mul64(l0_2, l2) + r2 = addMul64(r2, l1, l1) + r2 = addMul64(r2, l3_38, l4) + + // r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4 + r3 := mul64(l0_2, l3) + r3 = addMul64(r3, l1_2, l2) + r3 = addMul64(r3, l4_19, l4) + + // r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2 + r4 := mul64(l0_2, l4) + r4 = addMul64(r4, l1_2, l3) + r4 = addMul64(r4, l2, l2) + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + c4*19 + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + *v = Element{rr0, rr1, rr2, rr3, rr4} + v.carryPropagate() +} + +// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction +// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. +func (v *Element) carryPropagateGeneric() *Element { + c0 := v.l0 >> 51 + c1 := v.l1 >> 51 + c2 := v.l2 >> 51 + c3 := v.l3 >> 51 + c4 := v.l4 >> 51 + + // c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and + // the final l0 will be at most 52 bits. Similarly for the rest. + v.l0 = v.l0&maskLow51Bits + c4*19 + v.l1 = v.l1&maskLow51Bits + c0 + v.l2 = v.l2&maskLow51Bits + c1 + v.l3 = v.l3&maskLow51Bits + c2 + v.l4 = v.l4&maskLow51Bits + c3 + + return v +} diff --git a/vendor/filippo.io/edwards25519/scalar.go b/vendor/filippo.io/edwards25519/scalar.go new file mode 100644 index 00000000..3fd16538 --- /dev/null +++ b/vendor/filippo.io/edwards25519/scalar.go @@ -0,0 +1,343 @@ +// Copyright (c) 2016 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 edwards25519 + +import ( + "encoding/binary" + "errors" +) + +// A Scalar is an integer modulo +// +// l = 2^252 + 27742317777372353535851937790883648493 +// +// which is the prime order of the edwards25519 group. +// +// This type works similarly to math/big.Int, and all arguments and +// receivers are allowed to alias. +// +// The zero value is a valid zero element. +type Scalar struct { + // s is the scalar in the Montgomery domain, in the format of the + // fiat-crypto implementation. + s fiatScalarMontgomeryDomainFieldElement +} + +// The field implementation in scalar_fiat.go is generated by the fiat-crypto +// project (https://github.com/mit-plv/fiat-crypto) at version v0.0.9 (23d2dbc) +// from a formally verified model. +// +// fiat-crypto code comes under the following license. +// +// Copyright (c) 2015-2020 The fiat-crypto Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, +// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +// NewScalar returns a new zero Scalar. +func NewScalar() *Scalar { + return &Scalar{} +} + +// MultiplyAdd sets s = x * y + z mod l, and returns s. It is equivalent to +// using Multiply and then Add. +func (s *Scalar) MultiplyAdd(x, y, z *Scalar) *Scalar { + // Make a copy of z in case it aliases s. + zCopy := new(Scalar).Set(z) + return s.Multiply(x, y).Add(s, zCopy) +} + +// Add sets s = x + y mod l, and returns s. +func (s *Scalar) Add(x, y *Scalar) *Scalar { + // s = 1 * x + y mod l + fiatScalarAdd(&s.s, &x.s, &y.s) + return s +} + +// Subtract sets s = x - y mod l, and returns s. +func (s *Scalar) Subtract(x, y *Scalar) *Scalar { + // s = -1 * y + x mod l + fiatScalarSub(&s.s, &x.s, &y.s) + return s +} + +// Negate sets s = -x mod l, and returns s. +func (s *Scalar) Negate(x *Scalar) *Scalar { + // s = -1 * x + 0 mod l + fiatScalarOpp(&s.s, &x.s) + return s +} + +// Multiply sets s = x * y mod l, and returns s. +func (s *Scalar) Multiply(x, y *Scalar) *Scalar { + // s = x * y + 0 mod l + fiatScalarMul(&s.s, &x.s, &y.s) + return s +} + +// Set sets s = x, and returns s. +func (s *Scalar) Set(x *Scalar) *Scalar { + *s = *x + return s +} + +// SetUniformBytes sets s = x mod l, where x is a 64-byte little-endian integer. +// If x is not of the right length, SetUniformBytes returns nil and an error, +// and the receiver is unchanged. +// +// SetUniformBytes can be used to set s to a uniformly distributed value given +// 64 uniformly distributed random bytes. +func (s *Scalar) SetUniformBytes(x []byte) (*Scalar, error) { + if len(x) != 64 { + return nil, errors.New("edwards25519: invalid SetUniformBytes input length") + } + + // We have a value x of 512 bits, but our fiatScalarFromBytes function + // expects an input lower than l, which is a little over 252 bits. + // + // Instead of writing a reduction function that operates on wider inputs, we + // can interpret x as the sum of three shorter values a, b, and c. + // + // x = a + b * 2^168 + c * 2^336 mod l + // + // We then precompute 2^168 and 2^336 modulo l, and perform the reduction + // with two multiplications and two additions. + + s.setShortBytes(x[:21]) + t := new(Scalar).setShortBytes(x[21:42]) + s.Add(s, t.Multiply(t, scalarTwo168)) + t.setShortBytes(x[42:]) + s.Add(s, t.Multiply(t, scalarTwo336)) + + return s, nil +} + +// scalarTwo168 and scalarTwo336 are 2^168 and 2^336 modulo l, encoded as a +// fiatScalarMontgomeryDomainFieldElement, which is a little-endian 4-limb value +// in the 2^256 Montgomery domain. +var scalarTwo168 = &Scalar{s: [4]uint64{0x5b8ab432eac74798, 0x38afddd6de59d5d7, + 0xa2c131b399411b7c, 0x6329a7ed9ce5a30}} +var scalarTwo336 = &Scalar{s: [4]uint64{0xbd3d108e2b35ecc5, 0x5c3a3718bdf9c90b, + 0x63aa97a331b4f2ee, 0x3d217f5be65cb5c}} + +// setShortBytes sets s = x mod l, where x is a little-endian integer shorter +// than 32 bytes. +func (s *Scalar) setShortBytes(x []byte) *Scalar { + if len(x) >= 32 { + panic("edwards25519: internal error: setShortBytes called with a long string") + } + var buf [32]byte + copy(buf[:], x) + fiatScalarFromBytes((*[4]uint64)(&s.s), &buf) + fiatScalarToMontgomery(&s.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&s.s)) + return s +} + +// SetCanonicalBytes sets s = x, where x is a 32-byte little-endian encoding of +// s, and returns s. If x is not a canonical encoding of s, SetCanonicalBytes +// returns nil and an error, and the receiver is unchanged. +func (s *Scalar) SetCanonicalBytes(x []byte) (*Scalar, error) { + if len(x) != 32 { + return nil, errors.New("invalid scalar length") + } + if !isReduced(x) { + return nil, errors.New("invalid scalar encoding") + } + + fiatScalarFromBytes((*[4]uint64)(&s.s), (*[32]byte)(x)) + fiatScalarToMontgomery(&s.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&s.s)) + + return s, nil +} + +// scalarMinusOneBytes is l - 1 in little endian. +var scalarMinusOneBytes = [32]byte{236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16} + +// isReduced returns whether the given scalar in 32-byte little endian encoded +// form is reduced modulo l. +func isReduced(s []byte) bool { + if len(s) != 32 { + return false + } + + for i := len(s) - 1; i >= 0; i-- { + switch { + case s[i] > scalarMinusOneBytes[i]: + return false + case s[i] < scalarMinusOneBytes[i]: + return true + } + } + return true +} + +// SetBytesWithClamping applies the buffer pruning described in RFC 8032, +// Section 5.1.5 (also known as clamping) and sets s to the result. The input +// must be 32 bytes, and it is not modified. If x is not of the right length, +// SetBytesWithClamping returns nil and an error, and the receiver is unchanged. +// +// Note that since Scalar values are always reduced modulo the prime order of +// the curve, the resulting value will not preserve any of the cofactor-clearing +// properties that clamping is meant to provide. It will however work as +// expected as long as it is applied to points on the prime order subgroup, like +// in Ed25519. In fact, it is lost to history why RFC 8032 adopted the +// irrelevant RFC 7748 clamping, but it is now required for compatibility. +func (s *Scalar) SetBytesWithClamping(x []byte) (*Scalar, error) { + // The description above omits the purpose of the high bits of the clamping + // for brevity, but those are also lost to reductions, and are also + // irrelevant to edwards25519 as they protect against a specific + // implementation bug that was once observed in a generic Montgomery ladder. + if len(x) != 32 { + return nil, errors.New("edwards25519: invalid SetBytesWithClamping input length") + } + + // We need to use the wide reduction from SetUniformBytes, since clamping + // sets the 2^254 bit, making the value higher than the order. + var wideBytes [64]byte + copy(wideBytes[:], x[:]) + wideBytes[0] &= 248 + wideBytes[31] &= 63 + wideBytes[31] |= 64 + return s.SetUniformBytes(wideBytes[:]) +} + +// Bytes returns the canonical 32-byte little-endian encoding of s. +func (s *Scalar) Bytes() []byte { + // This function is outlined to make the allocations inline in the caller + // rather than happen on the heap. + var encoded [32]byte + return s.bytes(&encoded) +} + +func (s *Scalar) bytes(out *[32]byte) []byte { + var ss fiatScalarNonMontgomeryDomainFieldElement + fiatScalarFromMontgomery(&ss, &s.s) + fiatScalarToBytes(out, (*[4]uint64)(&ss)) + return out[:] +} + +// Equal returns 1 if s and t are equal, and 0 otherwise. +func (s *Scalar) Equal(t *Scalar) int { + var diff fiatScalarMontgomeryDomainFieldElement + fiatScalarSub(&diff, &s.s, &t.s) + var nonzero uint64 + fiatScalarNonzero(&nonzero, (*[4]uint64)(&diff)) + nonzero |= nonzero >> 32 + nonzero |= nonzero >> 16 + nonzero |= nonzero >> 8 + nonzero |= nonzero >> 4 + nonzero |= nonzero >> 2 + nonzero |= nonzero >> 1 + return int(^nonzero) & 1 +} + +// nonAdjacentForm computes a width-w non-adjacent form for this scalar. +// +// w must be between 2 and 8, or nonAdjacentForm will panic. +func (s *Scalar) nonAdjacentForm(w uint) [256]int8 { + // This implementation is adapted from the one + // in curve25519-dalek and is documented there: + // https://github.com/dalek-cryptography/curve25519-dalek/blob/f630041af28e9a405255f98a8a93adca18e4315b/src/scalar.rs#L800-L871 + b := s.Bytes() + if b[31] > 127 { + panic("scalar has high bit set illegally") + } + if w < 2 { + panic("w must be at least 2 by the definition of NAF") + } else if w > 8 { + panic("NAF digits must fit in int8") + } + + var naf [256]int8 + var digits [5]uint64 + + for i := 0; i < 4; i++ { + digits[i] = binary.LittleEndian.Uint64(b[i*8:]) + } + + width := uint64(1 << w) + windowMask := uint64(width - 1) + + pos := uint(0) + carry := uint64(0) + for pos < 256 { + indexU64 := pos / 64 + indexBit := pos % 64 + var bitBuf uint64 + if indexBit < 64-w { + // This window's bits are contained in a single u64 + bitBuf = digits[indexU64] >> indexBit + } else { + // Combine the current 64 bits with bits from the next 64 + bitBuf = (digits[indexU64] >> indexBit) | (digits[1+indexU64] << (64 - indexBit)) + } + + // Add carry into the current window + window := carry + (bitBuf & windowMask) + + if window&1 == 0 { + // If the window value is even, preserve the carry and continue. + // Why is the carry preserved? + // If carry == 0 and window & 1 == 0, + // then the next carry should be 0 + // If carry == 1 and window & 1 == 0, + // then bit_buf & 1 == 1 so the next carry should be 1 + pos += 1 + continue + } + + if window < width/2 { + carry = 0 + naf[pos] = int8(window) + } else { + carry = 1 + naf[pos] = int8(window) - int8(width) + } + + pos += w + } + return naf +} + +func (s *Scalar) signedRadix16() [64]int8 { + b := s.Bytes() + if b[31] > 127 { + panic("scalar has high bit set illegally") + } + + var digits [64]int8 + + // Compute unsigned radix-16 digits: + for i := 0; i < 32; i++ { + digits[2*i] = int8(b[i] & 15) + digits[2*i+1] = int8((b[i] >> 4) & 15) + } + + // Recenter coefficients: + for i := 0; i < 63; i++ { + carry := (digits[i] + 8) >> 4 + digits[i] -= carry << 4 + digits[i+1] += carry + } + + return digits +} diff --git a/vendor/filippo.io/edwards25519/scalar_fiat.go b/vendor/filippo.io/edwards25519/scalar_fiat.go new file mode 100644 index 00000000..2e5782b6 --- /dev/null +++ b/vendor/filippo.io/edwards25519/scalar_fiat.go @@ -0,0 +1,1147 @@ +// Code generated by Fiat Cryptography. DO NOT EDIT. +// +// Autogenerated: word_by_word_montgomery --lang Go --cmovznz-by-mul --relax-primitive-carry-to-bitwidth 32,64 --public-function-case camelCase --public-type-case camelCase --private-function-case camelCase --private-type-case camelCase --doc-text-before-function-name '' --doc-newline-before-package-declaration --doc-prepend-header 'Code generated by Fiat Cryptography. DO NOT EDIT.' --package-name edwards25519 Scalar 64 '2^252 + 27742317777372353535851937790883648493' mul add sub opp nonzero from_montgomery to_montgomery to_bytes from_bytes +// +// curve description: Scalar +// +// machine_wordsize = 64 (from "64") +// +// requested operations: mul, add, sub, opp, nonzero, from_montgomery, to_montgomery, to_bytes, from_bytes +// +// m = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed (from "2^252 + 27742317777372353535851937790883648493") +// +// +// +// NOTE: In addition to the bounds specified above each function, all +// +// functions synthesized for this Montgomery arithmetic require the +// +// input to be strictly less than the prime modulus (m), and also +// +// require the input to be in the unique saturated representation. +// +// All functions also ensure that these two properties are true of +// +// return values. +// +// +// +// Computed values: +// +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) +// +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) +// +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in +// +// if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 + +package edwards25519 + +import "math/bits" + +type fiatScalarUint1 uint64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927 +type fiatScalarInt1 int64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927 + +// The type fiatScalarMontgomeryDomainFieldElement is a field element in the Montgomery domain. +// +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +type fiatScalarMontgomeryDomainFieldElement [4]uint64 + +// The type fiatScalarNonMontgomeryDomainFieldElement is a field element NOT in the Montgomery domain. +// +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +type fiatScalarNonMontgomeryDomainFieldElement [4]uint64 + +// fiatScalarCmovznzU64 is a single-word conditional move. +// +// Postconditions: +// +// out1 = (if arg1 = 0 then arg2 else arg3) +// +// Input Bounds: +// +// arg1: [0x0 ~> 0x1] +// arg2: [0x0 ~> 0xffffffffffffffff] +// arg3: [0x0 ~> 0xffffffffffffffff] +// +// Output Bounds: +// +// out1: [0x0 ~> 0xffffffffffffffff] +func fiatScalarCmovznzU64(out1 *uint64, arg1 fiatScalarUint1, arg2 uint64, arg3 uint64) { + x1 := (uint64(arg1) * 0xffffffffffffffff) + x2 := ((x1 & arg3) | ((^x1) & arg2)) + *out1 = x2 +} + +// fiatScalarMul multiplies two field elements in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// 0 ≤ eval arg2 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +// 0 ≤ eval out1 < m +func fiatScalarMul(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + var x5 uint64 + var x6 uint64 + x6, x5 = bits.Mul64(x4, arg2[3]) + var x7 uint64 + var x8 uint64 + x8, x7 = bits.Mul64(x4, arg2[2]) + var x9 uint64 + var x10 uint64 + x10, x9 = bits.Mul64(x4, arg2[1]) + var x11 uint64 + var x12 uint64 + x12, x11 = bits.Mul64(x4, arg2[0]) + var x13 uint64 + var x14 uint64 + x13, x14 = bits.Add64(x12, x9, uint64(0x0)) + var x15 uint64 + var x16 uint64 + x15, x16 = bits.Add64(x10, x7, uint64(fiatScalarUint1(x14))) + var x17 uint64 + var x18 uint64 + x17, x18 = bits.Add64(x8, x5, uint64(fiatScalarUint1(x16))) + x19 := (uint64(fiatScalarUint1(x18)) + x6) + var x20 uint64 + _, x20 = bits.Mul64(x11, 0xd2b51da312547e1b) + var x22 uint64 + var x23 uint64 + x23, x22 = bits.Mul64(x20, 0x1000000000000000) + var x24 uint64 + var x25 uint64 + x25, x24 = bits.Mul64(x20, 0x14def9dea2f79cd6) + var x26 uint64 + var x27 uint64 + x27, x26 = bits.Mul64(x20, 0x5812631a5cf5d3ed) + var x28 uint64 + var x29 uint64 + x28, x29 = bits.Add64(x27, x24, uint64(0x0)) + x30 := (uint64(fiatScalarUint1(x29)) + x25) + var x32 uint64 + _, x32 = bits.Add64(x11, x26, uint64(0x0)) + var x33 uint64 + var x34 uint64 + x33, x34 = bits.Add64(x13, x28, uint64(fiatScalarUint1(x32))) + var x35 uint64 + var x36 uint64 + x35, x36 = bits.Add64(x15, x30, uint64(fiatScalarUint1(x34))) + var x37 uint64 + var x38 uint64 + x37, x38 = bits.Add64(x17, x22, uint64(fiatScalarUint1(x36))) + var x39 uint64 + var x40 uint64 + x39, x40 = bits.Add64(x19, x23, uint64(fiatScalarUint1(x38))) + var x41 uint64 + var x42 uint64 + x42, x41 = bits.Mul64(x1, arg2[3]) + var x43 uint64 + var x44 uint64 + x44, x43 = bits.Mul64(x1, arg2[2]) + var x45 uint64 + var x46 uint64 + x46, x45 = bits.Mul64(x1, arg2[1]) + var x47 uint64 + var x48 uint64 + x48, x47 = bits.Mul64(x1, arg2[0]) + var x49 uint64 + var x50 uint64 + x49, x50 = bits.Add64(x48, x45, uint64(0x0)) + var x51 uint64 + var x52 uint64 + x51, x52 = bits.Add64(x46, x43, uint64(fiatScalarUint1(x50))) + var x53 uint64 + var x54 uint64 + x53, x54 = bits.Add64(x44, x41, uint64(fiatScalarUint1(x52))) + x55 := (uint64(fiatScalarUint1(x54)) + x42) + var x56 uint64 + var x57 uint64 + x56, x57 = bits.Add64(x33, x47, uint64(0x0)) + var x58 uint64 + var x59 uint64 + x58, x59 = bits.Add64(x35, x49, uint64(fiatScalarUint1(x57))) + var x60 uint64 + var x61 uint64 + x60, x61 = bits.Add64(x37, x51, uint64(fiatScalarUint1(x59))) + var x62 uint64 + var x63 uint64 + x62, x63 = bits.Add64(x39, x53, uint64(fiatScalarUint1(x61))) + var x64 uint64 + var x65 uint64 + x64, x65 = bits.Add64(uint64(fiatScalarUint1(x40)), x55, uint64(fiatScalarUint1(x63))) + var x66 uint64 + _, x66 = bits.Mul64(x56, 0xd2b51da312547e1b) + var x68 uint64 + var x69 uint64 + x69, x68 = bits.Mul64(x66, 0x1000000000000000) + var x70 uint64 + var x71 uint64 + x71, x70 = bits.Mul64(x66, 0x14def9dea2f79cd6) + var x72 uint64 + var x73 uint64 + x73, x72 = bits.Mul64(x66, 0x5812631a5cf5d3ed) + var x74 uint64 + var x75 uint64 + x74, x75 = bits.Add64(x73, x70, uint64(0x0)) + x76 := (uint64(fiatScalarUint1(x75)) + x71) + var x78 uint64 + _, x78 = bits.Add64(x56, x72, uint64(0x0)) + var x79 uint64 + var x80 uint64 + x79, x80 = bits.Add64(x58, x74, uint64(fiatScalarUint1(x78))) + var x81 uint64 + var x82 uint64 + x81, x82 = bits.Add64(x60, x76, uint64(fiatScalarUint1(x80))) + var x83 uint64 + var x84 uint64 + x83, x84 = bits.Add64(x62, x68, uint64(fiatScalarUint1(x82))) + var x85 uint64 + var x86 uint64 + x85, x86 = bits.Add64(x64, x69, uint64(fiatScalarUint1(x84))) + x87 := (uint64(fiatScalarUint1(x86)) + uint64(fiatScalarUint1(x65))) + var x88 uint64 + var x89 uint64 + x89, x88 = bits.Mul64(x2, arg2[3]) + var x90 uint64 + var x91 uint64 + x91, x90 = bits.Mul64(x2, arg2[2]) + var x92 uint64 + var x93 uint64 + x93, x92 = bits.Mul64(x2, arg2[1]) + var x94 uint64 + var x95 uint64 + x95, x94 = bits.Mul64(x2, arg2[0]) + var x96 uint64 + var x97 uint64 + x96, x97 = bits.Add64(x95, x92, uint64(0x0)) + var x98 uint64 + var x99 uint64 + x98, x99 = bits.Add64(x93, x90, uint64(fiatScalarUint1(x97))) + var x100 uint64 + var x101 uint64 + x100, x101 = bits.Add64(x91, x88, uint64(fiatScalarUint1(x99))) + x102 := (uint64(fiatScalarUint1(x101)) + x89) + var x103 uint64 + var x104 uint64 + x103, x104 = bits.Add64(x79, x94, uint64(0x0)) + var x105 uint64 + var x106 uint64 + x105, x106 = bits.Add64(x81, x96, uint64(fiatScalarUint1(x104))) + var x107 uint64 + var x108 uint64 + x107, x108 = bits.Add64(x83, x98, uint64(fiatScalarUint1(x106))) + var x109 uint64 + var x110 uint64 + x109, x110 = bits.Add64(x85, x100, uint64(fiatScalarUint1(x108))) + var x111 uint64 + var x112 uint64 + x111, x112 = bits.Add64(x87, x102, uint64(fiatScalarUint1(x110))) + var x113 uint64 + _, x113 = bits.Mul64(x103, 0xd2b51da312547e1b) + var x115 uint64 + var x116 uint64 + x116, x115 = bits.Mul64(x113, 0x1000000000000000) + var x117 uint64 + var x118 uint64 + x118, x117 = bits.Mul64(x113, 0x14def9dea2f79cd6) + var x119 uint64 + var x120 uint64 + x120, x119 = bits.Mul64(x113, 0x5812631a5cf5d3ed) + var x121 uint64 + var x122 uint64 + x121, x122 = bits.Add64(x120, x117, uint64(0x0)) + x123 := (uint64(fiatScalarUint1(x122)) + x118) + var x125 uint64 + _, x125 = bits.Add64(x103, x119, uint64(0x0)) + var x126 uint64 + var x127 uint64 + x126, x127 = bits.Add64(x105, x121, uint64(fiatScalarUint1(x125))) + var x128 uint64 + var x129 uint64 + x128, x129 = bits.Add64(x107, x123, uint64(fiatScalarUint1(x127))) + var x130 uint64 + var x131 uint64 + x130, x131 = bits.Add64(x109, x115, uint64(fiatScalarUint1(x129))) + var x132 uint64 + var x133 uint64 + x132, x133 = bits.Add64(x111, x116, uint64(fiatScalarUint1(x131))) + x134 := (uint64(fiatScalarUint1(x133)) + uint64(fiatScalarUint1(x112))) + var x135 uint64 + var x136 uint64 + x136, x135 = bits.Mul64(x3, arg2[3]) + var x137 uint64 + var x138 uint64 + x138, x137 = bits.Mul64(x3, arg2[2]) + var x139 uint64 + var x140 uint64 + x140, x139 = bits.Mul64(x3, arg2[1]) + var x141 uint64 + var x142 uint64 + x142, x141 = bits.Mul64(x3, arg2[0]) + var x143 uint64 + var x144 uint64 + x143, x144 = bits.Add64(x142, x139, uint64(0x0)) + var x145 uint64 + var x146 uint64 + x145, x146 = bits.Add64(x140, x137, uint64(fiatScalarUint1(x144))) + var x147 uint64 + var x148 uint64 + x147, x148 = bits.Add64(x138, x135, uint64(fiatScalarUint1(x146))) + x149 := (uint64(fiatScalarUint1(x148)) + x136) + var x150 uint64 + var x151 uint64 + x150, x151 = bits.Add64(x126, x141, uint64(0x0)) + var x152 uint64 + var x153 uint64 + x152, x153 = bits.Add64(x128, x143, uint64(fiatScalarUint1(x151))) + var x154 uint64 + var x155 uint64 + x154, x155 = bits.Add64(x130, x145, uint64(fiatScalarUint1(x153))) + var x156 uint64 + var x157 uint64 + x156, x157 = bits.Add64(x132, x147, uint64(fiatScalarUint1(x155))) + var x158 uint64 + var x159 uint64 + x158, x159 = bits.Add64(x134, x149, uint64(fiatScalarUint1(x157))) + var x160 uint64 + _, x160 = bits.Mul64(x150, 0xd2b51da312547e1b) + var x162 uint64 + var x163 uint64 + x163, x162 = bits.Mul64(x160, 0x1000000000000000) + var x164 uint64 + var x165 uint64 + x165, x164 = bits.Mul64(x160, 0x14def9dea2f79cd6) + var x166 uint64 + var x167 uint64 + x167, x166 = bits.Mul64(x160, 0x5812631a5cf5d3ed) + var x168 uint64 + var x169 uint64 + x168, x169 = bits.Add64(x167, x164, uint64(0x0)) + x170 := (uint64(fiatScalarUint1(x169)) + x165) + var x172 uint64 + _, x172 = bits.Add64(x150, x166, uint64(0x0)) + var x173 uint64 + var x174 uint64 + x173, x174 = bits.Add64(x152, x168, uint64(fiatScalarUint1(x172))) + var x175 uint64 + var x176 uint64 + x175, x176 = bits.Add64(x154, x170, uint64(fiatScalarUint1(x174))) + var x177 uint64 + var x178 uint64 + x177, x178 = bits.Add64(x156, x162, uint64(fiatScalarUint1(x176))) + var x179 uint64 + var x180 uint64 + x179, x180 = bits.Add64(x158, x163, uint64(fiatScalarUint1(x178))) + x181 := (uint64(fiatScalarUint1(x180)) + uint64(fiatScalarUint1(x159))) + var x182 uint64 + var x183 uint64 + x182, x183 = bits.Sub64(x173, 0x5812631a5cf5d3ed, uint64(0x0)) + var x184 uint64 + var x185 uint64 + x184, x185 = bits.Sub64(x175, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x183))) + var x186 uint64 + var x187 uint64 + x186, x187 = bits.Sub64(x177, uint64(0x0), uint64(fiatScalarUint1(x185))) + var x188 uint64 + var x189 uint64 + x188, x189 = bits.Sub64(x179, 0x1000000000000000, uint64(fiatScalarUint1(x187))) + var x191 uint64 + _, x191 = bits.Sub64(x181, uint64(0x0), uint64(fiatScalarUint1(x189))) + var x192 uint64 + fiatScalarCmovznzU64(&x192, fiatScalarUint1(x191), x182, x173) + var x193 uint64 + fiatScalarCmovznzU64(&x193, fiatScalarUint1(x191), x184, x175) + var x194 uint64 + fiatScalarCmovznzU64(&x194, fiatScalarUint1(x191), x186, x177) + var x195 uint64 + fiatScalarCmovznzU64(&x195, fiatScalarUint1(x191), x188, x179) + out1[0] = x192 + out1[1] = x193 + out1[2] = x194 + out1[3] = x195 +} + +// fiatScalarAdd adds two field elements in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// 0 ≤ eval arg2 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +// 0 ≤ eval out1 < m +func fiatScalarAdd(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) { + var x1 uint64 + var x2 uint64 + x1, x2 = bits.Add64(arg1[0], arg2[0], uint64(0x0)) + var x3 uint64 + var x4 uint64 + x3, x4 = bits.Add64(arg1[1], arg2[1], uint64(fiatScalarUint1(x2))) + var x5 uint64 + var x6 uint64 + x5, x6 = bits.Add64(arg1[2], arg2[2], uint64(fiatScalarUint1(x4))) + var x7 uint64 + var x8 uint64 + x7, x8 = bits.Add64(arg1[3], arg2[3], uint64(fiatScalarUint1(x6))) + var x9 uint64 + var x10 uint64 + x9, x10 = bits.Sub64(x1, 0x5812631a5cf5d3ed, uint64(0x0)) + var x11 uint64 + var x12 uint64 + x11, x12 = bits.Sub64(x3, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x10))) + var x13 uint64 + var x14 uint64 + x13, x14 = bits.Sub64(x5, uint64(0x0), uint64(fiatScalarUint1(x12))) + var x15 uint64 + var x16 uint64 + x15, x16 = bits.Sub64(x7, 0x1000000000000000, uint64(fiatScalarUint1(x14))) + var x18 uint64 + _, x18 = bits.Sub64(uint64(fiatScalarUint1(x8)), uint64(0x0), uint64(fiatScalarUint1(x16))) + var x19 uint64 + fiatScalarCmovznzU64(&x19, fiatScalarUint1(x18), x9, x1) + var x20 uint64 + fiatScalarCmovznzU64(&x20, fiatScalarUint1(x18), x11, x3) + var x21 uint64 + fiatScalarCmovznzU64(&x21, fiatScalarUint1(x18), x13, x5) + var x22 uint64 + fiatScalarCmovznzU64(&x22, fiatScalarUint1(x18), x15, x7) + out1[0] = x19 + out1[1] = x20 + out1[2] = x21 + out1[3] = x22 +} + +// fiatScalarSub subtracts two field elements in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// 0 ≤ eval arg2 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +// 0 ≤ eval out1 < m +func fiatScalarSub(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) { + var x1 uint64 + var x2 uint64 + x1, x2 = bits.Sub64(arg1[0], arg2[0], uint64(0x0)) + var x3 uint64 + var x4 uint64 + x3, x4 = bits.Sub64(arg1[1], arg2[1], uint64(fiatScalarUint1(x2))) + var x5 uint64 + var x6 uint64 + x5, x6 = bits.Sub64(arg1[2], arg2[2], uint64(fiatScalarUint1(x4))) + var x7 uint64 + var x8 uint64 + x7, x8 = bits.Sub64(arg1[3], arg2[3], uint64(fiatScalarUint1(x6))) + var x9 uint64 + fiatScalarCmovznzU64(&x9, fiatScalarUint1(x8), uint64(0x0), 0xffffffffffffffff) + var x10 uint64 + var x11 uint64 + x10, x11 = bits.Add64(x1, (x9 & 0x5812631a5cf5d3ed), uint64(0x0)) + var x12 uint64 + var x13 uint64 + x12, x13 = bits.Add64(x3, (x9 & 0x14def9dea2f79cd6), uint64(fiatScalarUint1(x11))) + var x14 uint64 + var x15 uint64 + x14, x15 = bits.Add64(x5, uint64(0x0), uint64(fiatScalarUint1(x13))) + var x16 uint64 + x16, _ = bits.Add64(x7, (x9 & 0x1000000000000000), uint64(fiatScalarUint1(x15))) + out1[0] = x10 + out1[1] = x12 + out1[2] = x14 + out1[3] = x16 +} + +// fiatScalarOpp negates a field element in the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +// 0 ≤ eval out1 < m +func fiatScalarOpp(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement) { + var x1 uint64 + var x2 uint64 + x1, x2 = bits.Sub64(uint64(0x0), arg1[0], uint64(0x0)) + var x3 uint64 + var x4 uint64 + x3, x4 = bits.Sub64(uint64(0x0), arg1[1], uint64(fiatScalarUint1(x2))) + var x5 uint64 + var x6 uint64 + x5, x6 = bits.Sub64(uint64(0x0), arg1[2], uint64(fiatScalarUint1(x4))) + var x7 uint64 + var x8 uint64 + x7, x8 = bits.Sub64(uint64(0x0), arg1[3], uint64(fiatScalarUint1(x6))) + var x9 uint64 + fiatScalarCmovznzU64(&x9, fiatScalarUint1(x8), uint64(0x0), 0xffffffffffffffff) + var x10 uint64 + var x11 uint64 + x10, x11 = bits.Add64(x1, (x9 & 0x5812631a5cf5d3ed), uint64(0x0)) + var x12 uint64 + var x13 uint64 + x12, x13 = bits.Add64(x3, (x9 & 0x14def9dea2f79cd6), uint64(fiatScalarUint1(x11))) + var x14 uint64 + var x15 uint64 + x14, x15 = bits.Add64(x5, uint64(0x0), uint64(fiatScalarUint1(x13))) + var x16 uint64 + x16, _ = bits.Add64(x7, (x9 & 0x1000000000000000), uint64(fiatScalarUint1(x15))) + out1[0] = x10 + out1[1] = x12 + out1[2] = x14 + out1[3] = x16 +} + +// fiatScalarNonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +// +// Input Bounds: +// +// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +// +// Output Bounds: +// +// out1: [0x0 ~> 0xffffffffffffffff] +func fiatScalarNonzero(out1 *uint64, arg1 *[4]uint64) { + x1 := (arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))) + *out1 = x1 +} + +// fiatScalarFromMontgomery translates a field element out of the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m +// 0 ≤ eval out1 < m +func fiatScalarFromMontgomery(out1 *fiatScalarNonMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement) { + x1 := arg1[0] + var x2 uint64 + _, x2 = bits.Mul64(x1, 0xd2b51da312547e1b) + var x4 uint64 + var x5 uint64 + x5, x4 = bits.Mul64(x2, 0x1000000000000000) + var x6 uint64 + var x7 uint64 + x7, x6 = bits.Mul64(x2, 0x14def9dea2f79cd6) + var x8 uint64 + var x9 uint64 + x9, x8 = bits.Mul64(x2, 0x5812631a5cf5d3ed) + var x10 uint64 + var x11 uint64 + x10, x11 = bits.Add64(x9, x6, uint64(0x0)) + var x13 uint64 + _, x13 = bits.Add64(x1, x8, uint64(0x0)) + var x14 uint64 + var x15 uint64 + x14, x15 = bits.Add64(uint64(0x0), x10, uint64(fiatScalarUint1(x13))) + var x16 uint64 + var x17 uint64 + x16, x17 = bits.Add64(x14, arg1[1], uint64(0x0)) + var x18 uint64 + _, x18 = bits.Mul64(x16, 0xd2b51da312547e1b) + var x20 uint64 + var x21 uint64 + x21, x20 = bits.Mul64(x18, 0x1000000000000000) + var x22 uint64 + var x23 uint64 + x23, x22 = bits.Mul64(x18, 0x14def9dea2f79cd6) + var x24 uint64 + var x25 uint64 + x25, x24 = bits.Mul64(x18, 0x5812631a5cf5d3ed) + var x26 uint64 + var x27 uint64 + x26, x27 = bits.Add64(x25, x22, uint64(0x0)) + var x29 uint64 + _, x29 = bits.Add64(x16, x24, uint64(0x0)) + var x30 uint64 + var x31 uint64 + x30, x31 = bits.Add64((uint64(fiatScalarUint1(x17)) + (uint64(fiatScalarUint1(x15)) + (uint64(fiatScalarUint1(x11)) + x7))), x26, uint64(fiatScalarUint1(x29))) + var x32 uint64 + var x33 uint64 + x32, x33 = bits.Add64(x4, (uint64(fiatScalarUint1(x27)) + x23), uint64(fiatScalarUint1(x31))) + var x34 uint64 + var x35 uint64 + x34, x35 = bits.Add64(x5, x20, uint64(fiatScalarUint1(x33))) + var x36 uint64 + var x37 uint64 + x36, x37 = bits.Add64(x30, arg1[2], uint64(0x0)) + var x38 uint64 + var x39 uint64 + x38, x39 = bits.Add64(x32, uint64(0x0), uint64(fiatScalarUint1(x37))) + var x40 uint64 + var x41 uint64 + x40, x41 = bits.Add64(x34, uint64(0x0), uint64(fiatScalarUint1(x39))) + var x42 uint64 + _, x42 = bits.Mul64(x36, 0xd2b51da312547e1b) + var x44 uint64 + var x45 uint64 + x45, x44 = bits.Mul64(x42, 0x1000000000000000) + var x46 uint64 + var x47 uint64 + x47, x46 = bits.Mul64(x42, 0x14def9dea2f79cd6) + var x48 uint64 + var x49 uint64 + x49, x48 = bits.Mul64(x42, 0x5812631a5cf5d3ed) + var x50 uint64 + var x51 uint64 + x50, x51 = bits.Add64(x49, x46, uint64(0x0)) + var x53 uint64 + _, x53 = bits.Add64(x36, x48, uint64(0x0)) + var x54 uint64 + var x55 uint64 + x54, x55 = bits.Add64(x38, x50, uint64(fiatScalarUint1(x53))) + var x56 uint64 + var x57 uint64 + x56, x57 = bits.Add64(x40, (uint64(fiatScalarUint1(x51)) + x47), uint64(fiatScalarUint1(x55))) + var x58 uint64 + var x59 uint64 + x58, x59 = bits.Add64((uint64(fiatScalarUint1(x41)) + (uint64(fiatScalarUint1(x35)) + x21)), x44, uint64(fiatScalarUint1(x57))) + var x60 uint64 + var x61 uint64 + x60, x61 = bits.Add64(x54, arg1[3], uint64(0x0)) + var x62 uint64 + var x63 uint64 + x62, x63 = bits.Add64(x56, uint64(0x0), uint64(fiatScalarUint1(x61))) + var x64 uint64 + var x65 uint64 + x64, x65 = bits.Add64(x58, uint64(0x0), uint64(fiatScalarUint1(x63))) + var x66 uint64 + _, x66 = bits.Mul64(x60, 0xd2b51da312547e1b) + var x68 uint64 + var x69 uint64 + x69, x68 = bits.Mul64(x66, 0x1000000000000000) + var x70 uint64 + var x71 uint64 + x71, x70 = bits.Mul64(x66, 0x14def9dea2f79cd6) + var x72 uint64 + var x73 uint64 + x73, x72 = bits.Mul64(x66, 0x5812631a5cf5d3ed) + var x74 uint64 + var x75 uint64 + x74, x75 = bits.Add64(x73, x70, uint64(0x0)) + var x77 uint64 + _, x77 = bits.Add64(x60, x72, uint64(0x0)) + var x78 uint64 + var x79 uint64 + x78, x79 = bits.Add64(x62, x74, uint64(fiatScalarUint1(x77))) + var x80 uint64 + var x81 uint64 + x80, x81 = bits.Add64(x64, (uint64(fiatScalarUint1(x75)) + x71), uint64(fiatScalarUint1(x79))) + var x82 uint64 + var x83 uint64 + x82, x83 = bits.Add64((uint64(fiatScalarUint1(x65)) + (uint64(fiatScalarUint1(x59)) + x45)), x68, uint64(fiatScalarUint1(x81))) + x84 := (uint64(fiatScalarUint1(x83)) + x69) + var x85 uint64 + var x86 uint64 + x85, x86 = bits.Sub64(x78, 0x5812631a5cf5d3ed, uint64(0x0)) + var x87 uint64 + var x88 uint64 + x87, x88 = bits.Sub64(x80, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x86))) + var x89 uint64 + var x90 uint64 + x89, x90 = bits.Sub64(x82, uint64(0x0), uint64(fiatScalarUint1(x88))) + var x91 uint64 + var x92 uint64 + x91, x92 = bits.Sub64(x84, 0x1000000000000000, uint64(fiatScalarUint1(x90))) + var x94 uint64 + _, x94 = bits.Sub64(uint64(0x0), uint64(0x0), uint64(fiatScalarUint1(x92))) + var x95 uint64 + fiatScalarCmovznzU64(&x95, fiatScalarUint1(x94), x85, x78) + var x96 uint64 + fiatScalarCmovznzU64(&x96, fiatScalarUint1(x94), x87, x80) + var x97 uint64 + fiatScalarCmovznzU64(&x97, fiatScalarUint1(x94), x89, x82) + var x98 uint64 + fiatScalarCmovznzU64(&x98, fiatScalarUint1(x94), x91, x84) + out1[0] = x95 + out1[1] = x96 + out1[2] = x97 + out1[3] = x98 +} + +// fiatScalarToMontgomery translates a field element into the Montgomery domain. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// eval (from_montgomery out1) mod m = eval arg1 mod m +// 0 ≤ eval out1 < m +func fiatScalarToMontgomery(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarNonMontgomeryDomainFieldElement) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + var x5 uint64 + var x6 uint64 + x6, x5 = bits.Mul64(x4, 0x399411b7c309a3d) + var x7 uint64 + var x8 uint64 + x8, x7 = bits.Mul64(x4, 0xceec73d217f5be65) + var x9 uint64 + var x10 uint64 + x10, x9 = bits.Mul64(x4, 0xd00e1ba768859347) + var x11 uint64 + var x12 uint64 + x12, x11 = bits.Mul64(x4, 0xa40611e3449c0f01) + var x13 uint64 + var x14 uint64 + x13, x14 = bits.Add64(x12, x9, uint64(0x0)) + var x15 uint64 + var x16 uint64 + x15, x16 = bits.Add64(x10, x7, uint64(fiatScalarUint1(x14))) + var x17 uint64 + var x18 uint64 + x17, x18 = bits.Add64(x8, x5, uint64(fiatScalarUint1(x16))) + var x19 uint64 + _, x19 = bits.Mul64(x11, 0xd2b51da312547e1b) + var x21 uint64 + var x22 uint64 + x22, x21 = bits.Mul64(x19, 0x1000000000000000) + var x23 uint64 + var x24 uint64 + x24, x23 = bits.Mul64(x19, 0x14def9dea2f79cd6) + var x25 uint64 + var x26 uint64 + x26, x25 = bits.Mul64(x19, 0x5812631a5cf5d3ed) + var x27 uint64 + var x28 uint64 + x27, x28 = bits.Add64(x26, x23, uint64(0x0)) + var x30 uint64 + _, x30 = bits.Add64(x11, x25, uint64(0x0)) + var x31 uint64 + var x32 uint64 + x31, x32 = bits.Add64(x13, x27, uint64(fiatScalarUint1(x30))) + var x33 uint64 + var x34 uint64 + x33, x34 = bits.Add64(x15, (uint64(fiatScalarUint1(x28)) + x24), uint64(fiatScalarUint1(x32))) + var x35 uint64 + var x36 uint64 + x35, x36 = bits.Add64(x17, x21, uint64(fiatScalarUint1(x34))) + var x37 uint64 + var x38 uint64 + x38, x37 = bits.Mul64(x1, 0x399411b7c309a3d) + var x39 uint64 + var x40 uint64 + x40, x39 = bits.Mul64(x1, 0xceec73d217f5be65) + var x41 uint64 + var x42 uint64 + x42, x41 = bits.Mul64(x1, 0xd00e1ba768859347) + var x43 uint64 + var x44 uint64 + x44, x43 = bits.Mul64(x1, 0xa40611e3449c0f01) + var x45 uint64 + var x46 uint64 + x45, x46 = bits.Add64(x44, x41, uint64(0x0)) + var x47 uint64 + var x48 uint64 + x47, x48 = bits.Add64(x42, x39, uint64(fiatScalarUint1(x46))) + var x49 uint64 + var x50 uint64 + x49, x50 = bits.Add64(x40, x37, uint64(fiatScalarUint1(x48))) + var x51 uint64 + var x52 uint64 + x51, x52 = bits.Add64(x31, x43, uint64(0x0)) + var x53 uint64 + var x54 uint64 + x53, x54 = bits.Add64(x33, x45, uint64(fiatScalarUint1(x52))) + var x55 uint64 + var x56 uint64 + x55, x56 = bits.Add64(x35, x47, uint64(fiatScalarUint1(x54))) + var x57 uint64 + var x58 uint64 + x57, x58 = bits.Add64(((uint64(fiatScalarUint1(x36)) + (uint64(fiatScalarUint1(x18)) + x6)) + x22), x49, uint64(fiatScalarUint1(x56))) + var x59 uint64 + _, x59 = bits.Mul64(x51, 0xd2b51da312547e1b) + var x61 uint64 + var x62 uint64 + x62, x61 = bits.Mul64(x59, 0x1000000000000000) + var x63 uint64 + var x64 uint64 + x64, x63 = bits.Mul64(x59, 0x14def9dea2f79cd6) + var x65 uint64 + var x66 uint64 + x66, x65 = bits.Mul64(x59, 0x5812631a5cf5d3ed) + var x67 uint64 + var x68 uint64 + x67, x68 = bits.Add64(x66, x63, uint64(0x0)) + var x70 uint64 + _, x70 = bits.Add64(x51, x65, uint64(0x0)) + var x71 uint64 + var x72 uint64 + x71, x72 = bits.Add64(x53, x67, uint64(fiatScalarUint1(x70))) + var x73 uint64 + var x74 uint64 + x73, x74 = bits.Add64(x55, (uint64(fiatScalarUint1(x68)) + x64), uint64(fiatScalarUint1(x72))) + var x75 uint64 + var x76 uint64 + x75, x76 = bits.Add64(x57, x61, uint64(fiatScalarUint1(x74))) + var x77 uint64 + var x78 uint64 + x78, x77 = bits.Mul64(x2, 0x399411b7c309a3d) + var x79 uint64 + var x80 uint64 + x80, x79 = bits.Mul64(x2, 0xceec73d217f5be65) + var x81 uint64 + var x82 uint64 + x82, x81 = bits.Mul64(x2, 0xd00e1ba768859347) + var x83 uint64 + var x84 uint64 + x84, x83 = bits.Mul64(x2, 0xa40611e3449c0f01) + var x85 uint64 + var x86 uint64 + x85, x86 = bits.Add64(x84, x81, uint64(0x0)) + var x87 uint64 + var x88 uint64 + x87, x88 = bits.Add64(x82, x79, uint64(fiatScalarUint1(x86))) + var x89 uint64 + var x90 uint64 + x89, x90 = bits.Add64(x80, x77, uint64(fiatScalarUint1(x88))) + var x91 uint64 + var x92 uint64 + x91, x92 = bits.Add64(x71, x83, uint64(0x0)) + var x93 uint64 + var x94 uint64 + x93, x94 = bits.Add64(x73, x85, uint64(fiatScalarUint1(x92))) + var x95 uint64 + var x96 uint64 + x95, x96 = bits.Add64(x75, x87, uint64(fiatScalarUint1(x94))) + var x97 uint64 + var x98 uint64 + x97, x98 = bits.Add64(((uint64(fiatScalarUint1(x76)) + (uint64(fiatScalarUint1(x58)) + (uint64(fiatScalarUint1(x50)) + x38))) + x62), x89, uint64(fiatScalarUint1(x96))) + var x99 uint64 + _, x99 = bits.Mul64(x91, 0xd2b51da312547e1b) + var x101 uint64 + var x102 uint64 + x102, x101 = bits.Mul64(x99, 0x1000000000000000) + var x103 uint64 + var x104 uint64 + x104, x103 = bits.Mul64(x99, 0x14def9dea2f79cd6) + var x105 uint64 + var x106 uint64 + x106, x105 = bits.Mul64(x99, 0x5812631a5cf5d3ed) + var x107 uint64 + var x108 uint64 + x107, x108 = bits.Add64(x106, x103, uint64(0x0)) + var x110 uint64 + _, x110 = bits.Add64(x91, x105, uint64(0x0)) + var x111 uint64 + var x112 uint64 + x111, x112 = bits.Add64(x93, x107, uint64(fiatScalarUint1(x110))) + var x113 uint64 + var x114 uint64 + x113, x114 = bits.Add64(x95, (uint64(fiatScalarUint1(x108)) + x104), uint64(fiatScalarUint1(x112))) + var x115 uint64 + var x116 uint64 + x115, x116 = bits.Add64(x97, x101, uint64(fiatScalarUint1(x114))) + var x117 uint64 + var x118 uint64 + x118, x117 = bits.Mul64(x3, 0x399411b7c309a3d) + var x119 uint64 + var x120 uint64 + x120, x119 = bits.Mul64(x3, 0xceec73d217f5be65) + var x121 uint64 + var x122 uint64 + x122, x121 = bits.Mul64(x3, 0xd00e1ba768859347) + var x123 uint64 + var x124 uint64 + x124, x123 = bits.Mul64(x3, 0xa40611e3449c0f01) + var x125 uint64 + var x126 uint64 + x125, x126 = bits.Add64(x124, x121, uint64(0x0)) + var x127 uint64 + var x128 uint64 + x127, x128 = bits.Add64(x122, x119, uint64(fiatScalarUint1(x126))) + var x129 uint64 + var x130 uint64 + x129, x130 = bits.Add64(x120, x117, uint64(fiatScalarUint1(x128))) + var x131 uint64 + var x132 uint64 + x131, x132 = bits.Add64(x111, x123, uint64(0x0)) + var x133 uint64 + var x134 uint64 + x133, x134 = bits.Add64(x113, x125, uint64(fiatScalarUint1(x132))) + var x135 uint64 + var x136 uint64 + x135, x136 = bits.Add64(x115, x127, uint64(fiatScalarUint1(x134))) + var x137 uint64 + var x138 uint64 + x137, x138 = bits.Add64(((uint64(fiatScalarUint1(x116)) + (uint64(fiatScalarUint1(x98)) + (uint64(fiatScalarUint1(x90)) + x78))) + x102), x129, uint64(fiatScalarUint1(x136))) + var x139 uint64 + _, x139 = bits.Mul64(x131, 0xd2b51da312547e1b) + var x141 uint64 + var x142 uint64 + x142, x141 = bits.Mul64(x139, 0x1000000000000000) + var x143 uint64 + var x144 uint64 + x144, x143 = bits.Mul64(x139, 0x14def9dea2f79cd6) + var x145 uint64 + var x146 uint64 + x146, x145 = bits.Mul64(x139, 0x5812631a5cf5d3ed) + var x147 uint64 + var x148 uint64 + x147, x148 = bits.Add64(x146, x143, uint64(0x0)) + var x150 uint64 + _, x150 = bits.Add64(x131, x145, uint64(0x0)) + var x151 uint64 + var x152 uint64 + x151, x152 = bits.Add64(x133, x147, uint64(fiatScalarUint1(x150))) + var x153 uint64 + var x154 uint64 + x153, x154 = bits.Add64(x135, (uint64(fiatScalarUint1(x148)) + x144), uint64(fiatScalarUint1(x152))) + var x155 uint64 + var x156 uint64 + x155, x156 = bits.Add64(x137, x141, uint64(fiatScalarUint1(x154))) + x157 := ((uint64(fiatScalarUint1(x156)) + (uint64(fiatScalarUint1(x138)) + (uint64(fiatScalarUint1(x130)) + x118))) + x142) + var x158 uint64 + var x159 uint64 + x158, x159 = bits.Sub64(x151, 0x5812631a5cf5d3ed, uint64(0x0)) + var x160 uint64 + var x161 uint64 + x160, x161 = bits.Sub64(x153, 0x14def9dea2f79cd6, uint64(fiatScalarUint1(x159))) + var x162 uint64 + var x163 uint64 + x162, x163 = bits.Sub64(x155, uint64(0x0), uint64(fiatScalarUint1(x161))) + var x164 uint64 + var x165 uint64 + x164, x165 = bits.Sub64(x157, 0x1000000000000000, uint64(fiatScalarUint1(x163))) + var x167 uint64 + _, x167 = bits.Sub64(uint64(0x0), uint64(0x0), uint64(fiatScalarUint1(x165))) + var x168 uint64 + fiatScalarCmovznzU64(&x168, fiatScalarUint1(x167), x158, x151) + var x169 uint64 + fiatScalarCmovznzU64(&x169, fiatScalarUint1(x167), x160, x153) + var x170 uint64 + fiatScalarCmovznzU64(&x170, fiatScalarUint1(x167), x162, x155) + var x171 uint64 + fiatScalarCmovznzU64(&x171, fiatScalarUint1(x167), x164, x157) + out1[0] = x168 + out1[1] = x169 + out1[2] = x170 + out1[3] = x171 +} + +// fiatScalarToBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +// +// Preconditions: +// +// 0 ≤ eval arg1 < m +// +// Postconditions: +// +// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31] +// +// Input Bounds: +// +// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]] +// +// Output Bounds: +// +// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]] +func fiatScalarToBytes(out1 *[32]uint8, arg1 *[4]uint64) { + x1 := arg1[3] + x2 := arg1[2] + x3 := arg1[1] + x4 := arg1[0] + x5 := (uint8(x4) & 0xff) + x6 := (x4 >> 8) + x7 := (uint8(x6) & 0xff) + x8 := (x6 >> 8) + x9 := (uint8(x8) & 0xff) + x10 := (x8 >> 8) + x11 := (uint8(x10) & 0xff) + x12 := (x10 >> 8) + x13 := (uint8(x12) & 0xff) + x14 := (x12 >> 8) + x15 := (uint8(x14) & 0xff) + x16 := (x14 >> 8) + x17 := (uint8(x16) & 0xff) + x18 := uint8((x16 >> 8)) + x19 := (uint8(x3) & 0xff) + x20 := (x3 >> 8) + x21 := (uint8(x20) & 0xff) + x22 := (x20 >> 8) + x23 := (uint8(x22) & 0xff) + x24 := (x22 >> 8) + x25 := (uint8(x24) & 0xff) + x26 := (x24 >> 8) + x27 := (uint8(x26) & 0xff) + x28 := (x26 >> 8) + x29 := (uint8(x28) & 0xff) + x30 := (x28 >> 8) + x31 := (uint8(x30) & 0xff) + x32 := uint8((x30 >> 8)) + x33 := (uint8(x2) & 0xff) + x34 := (x2 >> 8) + x35 := (uint8(x34) & 0xff) + x36 := (x34 >> 8) + x37 := (uint8(x36) & 0xff) + x38 := (x36 >> 8) + x39 := (uint8(x38) & 0xff) + x40 := (x38 >> 8) + x41 := (uint8(x40) & 0xff) + x42 := (x40 >> 8) + x43 := (uint8(x42) & 0xff) + x44 := (x42 >> 8) + x45 := (uint8(x44) & 0xff) + x46 := uint8((x44 >> 8)) + x47 := (uint8(x1) & 0xff) + x48 := (x1 >> 8) + x49 := (uint8(x48) & 0xff) + x50 := (x48 >> 8) + x51 := (uint8(x50) & 0xff) + x52 := (x50 >> 8) + x53 := (uint8(x52) & 0xff) + x54 := (x52 >> 8) + x55 := (uint8(x54) & 0xff) + x56 := (x54 >> 8) + x57 := (uint8(x56) & 0xff) + x58 := (x56 >> 8) + x59 := (uint8(x58) & 0xff) + x60 := uint8((x58 >> 8)) + out1[0] = x5 + out1[1] = x7 + out1[2] = x9 + out1[3] = x11 + out1[4] = x13 + out1[5] = x15 + out1[6] = x17 + out1[7] = x18 + out1[8] = x19 + out1[9] = x21 + out1[10] = x23 + out1[11] = x25 + out1[12] = x27 + out1[13] = x29 + out1[14] = x31 + out1[15] = x32 + out1[16] = x33 + out1[17] = x35 + out1[18] = x37 + out1[19] = x39 + out1[20] = x41 + out1[21] = x43 + out1[22] = x45 + out1[23] = x46 + out1[24] = x47 + out1[25] = x49 + out1[26] = x51 + out1[27] = x53 + out1[28] = x55 + out1[29] = x57 + out1[30] = x59 + out1[31] = x60 +} + +// fiatScalarFromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +// +// Preconditions: +// +// 0 ≤ bytes_eval arg1 < m +// +// Postconditions: +// +// eval out1 mod m = bytes_eval arg1 mod m +// 0 ≤ eval out1 < m +// +// Input Bounds: +// +// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]] +// +// Output Bounds: +// +// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]] +func fiatScalarFromBytes(out1 *[4]uint64, arg1 *[32]uint8) { + x1 := (uint64(arg1[31]) << 56) + x2 := (uint64(arg1[30]) << 48) + x3 := (uint64(arg1[29]) << 40) + x4 := (uint64(arg1[28]) << 32) + x5 := (uint64(arg1[27]) << 24) + x6 := (uint64(arg1[26]) << 16) + x7 := (uint64(arg1[25]) << 8) + x8 := arg1[24] + x9 := (uint64(arg1[23]) << 56) + x10 := (uint64(arg1[22]) << 48) + x11 := (uint64(arg1[21]) << 40) + x12 := (uint64(arg1[20]) << 32) + x13 := (uint64(arg1[19]) << 24) + x14 := (uint64(arg1[18]) << 16) + x15 := (uint64(arg1[17]) << 8) + x16 := arg1[16] + x17 := (uint64(arg1[15]) << 56) + x18 := (uint64(arg1[14]) << 48) + x19 := (uint64(arg1[13]) << 40) + x20 := (uint64(arg1[12]) << 32) + x21 := (uint64(arg1[11]) << 24) + x22 := (uint64(arg1[10]) << 16) + x23 := (uint64(arg1[9]) << 8) + x24 := arg1[8] + x25 := (uint64(arg1[7]) << 56) + x26 := (uint64(arg1[6]) << 48) + x27 := (uint64(arg1[5]) << 40) + x28 := (uint64(arg1[4]) << 32) + x29 := (uint64(arg1[3]) << 24) + x30 := (uint64(arg1[2]) << 16) + x31 := (uint64(arg1[1]) << 8) + x32 := arg1[0] + x33 := (x31 + uint64(x32)) + x34 := (x30 + x33) + x35 := (x29 + x34) + x36 := (x28 + x35) + x37 := (x27 + x36) + x38 := (x26 + x37) + x39 := (x25 + x38) + x40 := (x23 + uint64(x24)) + x41 := (x22 + x40) + x42 := (x21 + x41) + x43 := (x20 + x42) + x44 := (x19 + x43) + x45 := (x18 + x44) + x46 := (x17 + x45) + x47 := (x15 + uint64(x16)) + x48 := (x14 + x47) + x49 := (x13 + x48) + x50 := (x12 + x49) + x51 := (x11 + x50) + x52 := (x10 + x51) + x53 := (x9 + x52) + x54 := (x7 + uint64(x8)) + x55 := (x6 + x54) + x56 := (x5 + x55) + x57 := (x4 + x56) + x58 := (x3 + x57) + x59 := (x2 + x58) + x60 := (x1 + x59) + out1[0] = x39 + out1[1] = x46 + out1[2] = x53 + out1[3] = x60 +} diff --git a/vendor/filippo.io/edwards25519/scalarmult.go b/vendor/filippo.io/edwards25519/scalarmult.go new file mode 100644 index 00000000..f7ca3cef --- /dev/null +++ b/vendor/filippo.io/edwards25519/scalarmult.go @@ -0,0 +1,214 @@ +// Copyright (c) 2019 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 edwards25519 + +import "sync" + +// basepointTable is a set of 32 affineLookupTables, where table i is generated +// from 256i * basepoint. It is precomputed the first time it's used. +func basepointTable() *[32]affineLookupTable { + basepointTablePrecomp.initOnce.Do(func() { + p := NewGeneratorPoint() + for i := 0; i < 32; i++ { + basepointTablePrecomp.table[i].FromP3(p) + for j := 0; j < 8; j++ { + p.Add(p, p) + } + } + }) + return &basepointTablePrecomp.table +} + +var basepointTablePrecomp struct { + table [32]affineLookupTable + initOnce sync.Once +} + +// ScalarBaseMult sets v = x * B, where B is the canonical generator, and +// returns v. +// +// The scalar multiplication is done in constant time. +func (v *Point) ScalarBaseMult(x *Scalar) *Point { + basepointTable := basepointTable() + + // Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i ) + // as described in the Ed25519 paper + // + // Group even and odd coefficients + // x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B + // + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B + // x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B + // + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B) + // + // We use a lookup table for each i to get x_i*16^(2*i)*B + // and do four doublings to multiply by 16. + digits := x.signedRadix16() + + multiple := &affineCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + + // Accumulate the odd components first + v.Set(NewIdentityPoint()) + for i := 1; i < 64; i += 2 { + basepointTable[i/2].SelectInto(multiple, digits[i]) + tmp1.AddAffine(v, multiple) + v.fromP1xP1(tmp1) + } + + // Multiply by 16 + tmp2.FromP3(v) // tmp2 = v in P2 coords + tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords + v.fromP1xP1(tmp1) // now v = 16*(odd components) + + // Accumulate the even components + for i := 0; i < 64; i += 2 { + basepointTable[i/2].SelectInto(multiple, digits[i]) + tmp1.AddAffine(v, multiple) + v.fromP1xP1(tmp1) + } + + return v +} + +// ScalarMult sets v = x * q, and returns v. +// +// The scalar multiplication is done in constant time. +func (v *Point) ScalarMult(x *Scalar, q *Point) *Point { + checkInitialized(q) + + var table projLookupTable + table.FromP3(q) + + // Write x = sum(x_i * 16^i) + // so x*Q = sum( Q*x_i*16^i ) + // = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... ) + // <------compute inside out--------- + // + // We use the lookup table to get the x_i*Q values + // and do four doublings to compute 16*Q + digits := x.signedRadix16() + + // Unwrap first loop iteration to save computing 16*identity + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + table.SelectInto(multiple, digits[63]) + + v.Set(NewIdentityPoint()) + tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords + for i := 62; i >= 0; i-- { + tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords + v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords + table.SelectInto(multiple, digits[i]) + tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords + } + v.fromP1xP1(tmp1) + return v +} + +// basepointNafTable is the nafLookupTable8 for the basepoint. +// It is precomputed the first time it's used. +func basepointNafTable() *nafLookupTable8 { + basepointNafTablePrecomp.initOnce.Do(func() { + basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint()) + }) + return &basepointNafTablePrecomp.table +} + +var basepointNafTablePrecomp struct { + table nafLookupTable8 + initOnce sync.Once +} + +// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical +// generator, and returns v. +// +// Execution time depends on the inputs. +func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point { + checkInitialized(A) + + // Similarly to the single variable-base approach, we compute + // digits and use them with a lookup table. However, because + // we are allowed to do variable-time operations, we don't + // need constant-time lookups or constant-time digit + // computations. + // + // So we use a non-adjacent form of some width w instead of + // radix 16. This is like a binary representation (one digit + // for each binary place) but we allow the digits to grow in + // magnitude up to 2^{w-1} so that the nonzero digits are as + // sparse as possible. Intuitively, this "condenses" the + // "mass" of the scalar onto sparse coefficients (meaning + // fewer additions). + + basepointNafTable := basepointNafTable() + var aTable nafLookupTable5 + aTable.FromP3(A) + // Because the basepoint is fixed, we can use a wider NAF + // corresponding to a bigger table. + aNaf := a.nonAdjacentForm(5) + bNaf := b.nonAdjacentForm(8) + + // Find the first nonzero coefficient. + i := 255 + for j := i; j >= 0; j-- { + if aNaf[j] != 0 || bNaf[j] != 0 { + break + } + } + + multA := &projCached{} + multB := &affineCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + tmp2.Zero() + + // Move from high to low bits, doubling the accumulator + // at each iteration and checking whether there is a nonzero + // coefficient to look up a multiple of. + for ; i >= 0; i-- { + tmp1.Double(tmp2) + + // Only update v if we have a nonzero coeff to add in. + if aNaf[i] > 0 { + v.fromP1xP1(tmp1) + aTable.SelectInto(multA, aNaf[i]) + tmp1.Add(v, multA) + } else if aNaf[i] < 0 { + v.fromP1xP1(tmp1) + aTable.SelectInto(multA, -aNaf[i]) + tmp1.Sub(v, multA) + } + + if bNaf[i] > 0 { + v.fromP1xP1(tmp1) + basepointNafTable.SelectInto(multB, bNaf[i]) + tmp1.AddAffine(v, multB) + } else if bNaf[i] < 0 { + v.fromP1xP1(tmp1) + basepointNafTable.SelectInto(multB, -bNaf[i]) + tmp1.SubAffine(v, multB) + } + + tmp2.FromP1xP1(tmp1) + } + + v.fromP2(tmp2) + return v +} diff --git a/vendor/filippo.io/edwards25519/tables.go b/vendor/filippo.io/edwards25519/tables.go new file mode 100644 index 00000000..83234bbc --- /dev/null +++ b/vendor/filippo.io/edwards25519/tables.go @@ -0,0 +1,129 @@ +// Copyright (c) 2019 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 edwards25519 + +import ( + "crypto/subtle" +) + +// A dynamic lookup table for variable-base, constant-time scalar muls. +type projLookupTable struct { + points [8]projCached +} + +// A precomputed lookup table for fixed-base, constant-time scalar muls. +type affineLookupTable struct { + points [8]affineCached +} + +// A dynamic lookup table for variable-base, variable-time scalar muls. +type nafLookupTable5 struct { + points [8]projCached +} + +// A precomputed lookup table for fixed-base, variable-time scalar muls. +type nafLookupTable8 struct { + points [64]affineCached +} + +// Constructors. + +// Builds a lookup table at runtime. Fast. +func (v *projLookupTable) FromP3(q *Point) { + // Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q + // This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q + v.points[0].FromP3(q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 7; i++ { + // Compute (i+1)*Q as Q + i*Q and convert to a projCached + // This is needlessly complicated because the API has explicit + // receivers instead of creating stack objects and relying on RVO + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i]))) + } +} + +// This is not optimised for speed; fixed-base tables should be precomputed. +func (v *affineLookupTable) FromP3(q *Point) { + // Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q + // This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q + v.points[0].FromP3(q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 7; i++ { + // Compute (i+1)*Q as Q + i*Q and convert to affineCached + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i]))) + } +} + +// Builds a lookup table at runtime. Fast. +func (v *nafLookupTable5) FromP3(q *Point) { + // Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q + // This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q + v.points[0].FromP3(q) + q2 := Point{} + q2.Add(q, q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 7; i++ { + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i]))) + } +} + +// This is not optimised for speed; fixed-base tables should be precomputed. +func (v *nafLookupTable8) FromP3(q *Point) { + v.points[0].FromP3(q) + q2 := Point{} + q2.Add(q, q) + tmpP3 := Point{} + tmpP1xP1 := projP1xP1{} + for i := 0; i < 63; i++ { + v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i]))) + } +} + +// Selectors. + +// Set dest to x*Q, where -8 <= x <= 8, in constant time. +func (v *projLookupTable) SelectInto(dest *projCached, x int8) { + // Compute xabs = |x| + xmask := x >> 7 + xabs := uint8((x + xmask) ^ xmask) + + dest.Zero() + for j := 1; j <= 8; j++ { + // Set dest = j*Q if |x| = j + cond := subtle.ConstantTimeByteEq(xabs, uint8(j)) + dest.Select(&v.points[j-1], dest, cond) + } + // Now dest = |x|*Q, conditionally negate to get x*Q + dest.CondNeg(int(xmask & 1)) +} + +// Set dest to x*Q, where -8 <= x <= 8, in constant time. +func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) { + // Compute xabs = |x| + xmask := x >> 7 + xabs := uint8((x + xmask) ^ xmask) + + dest.Zero() + for j := 1; j <= 8; j++ { + // Set dest = j*Q if |x| = j + cond := subtle.ConstantTimeByteEq(xabs, uint8(j)) + dest.Select(&v.points[j-1], dest, cond) + } + // Now dest = |x|*Q, conditionally negate to get x*Q + dest.CondNeg(int(xmask & 1)) +} + +// Given odd x with 0 < x < 2^4, return x*Q (in variable time). +func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) { + *dest = v.points[x/2] +} + +// Given odd x with 0 < x < 2^7, return x*Q (in variable time). +func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) { + *dest = v.points[x/2] +} diff --git a/vendor/github.com/matrix-org/gomatrix/.gitignore b/vendor/github.com/matrix-org/gomatrix/.gitignore deleted file mode 100644 index 0dd56286..00000000 --- a/vendor/github.com/matrix-org/gomatrix/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so -*.out - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -# test editor files -*.swp diff --git a/vendor/github.com/matrix-org/gomatrix/.golangci.yml b/vendor/github.com/matrix-org/gomatrix/.golangci.yml deleted file mode 100644 index 15eb6ef7..00000000 --- a/vendor/github.com/matrix-org/gomatrix/.golangci.yml +++ /dev/null @@ -1,21 +0,0 @@ -run: - timeout: 5m - linters: - enable: - - vet - - vetshadow - - typecheck - - deadcode - - gocyclo - - golint - - varcheck - - structcheck - - maligned - - ineffassign - - misspell - - unparam - - goimports - - goconst - - unconvert - - errcheck - - interfacer diff --git a/vendor/github.com/matrix-org/gomatrix/.travis.yml b/vendor/github.com/matrix-org/gomatrix/.travis.yml deleted file mode 100644 index 46997ab6..00000000 --- a/vendor/github.com/matrix-org/gomatrix/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go -go: - - 1.13.10 -install: - - go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.24.0 - - go build -script: ./hooks/pre-commit diff --git a/vendor/github.com/matrix-org/gomatrix/CHANGELOG.md b/vendor/github.com/matrix-org/gomatrix/CHANGELOG.md deleted file mode 100644 index b6a9a167..00000000 --- a/vendor/github.com/matrix-org/gomatrix/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -## Release 0.1.0 (UNRELEASED) diff --git a/vendor/github.com/matrix-org/gomatrix/LICENSE b/vendor/github.com/matrix-org/gomatrix/LICENSE deleted file mode 100644 index 8dada3ed..00000000 --- a/vendor/github.com/matrix-org/gomatrix/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/matrix-org/gomatrix/README.md b/vendor/github.com/matrix-org/gomatrix/README.md deleted file mode 100644 index a083b46c..00000000 --- a/vendor/github.com/matrix-org/gomatrix/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# gomatrix -[![GoDoc](https://godoc.org/github.com/matrix-org/gomatrix?status.svg)](https://godoc.org/github.com/matrix-org/gomatrix) - -A Golang Matrix client. - -**THIS IS UNDER ACTIVE DEVELOPMENT: BREAKING CHANGES ARE FREQUENT.** - -# Contributing - -All contributions are greatly appreciated! - -## How to report issues - -Please check the current open issues for similar reports -in order to avoid duplicates. - -Some general guidelines: - -- Include a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) when possible. -- Describe the expected behaviour and what actually happened - including a full trace-back in case of exceptions. -- Make sure to list details about your environment - -## Setting up your environment - -If you intend to contribute to gomatrix you'll first need Go installed on your machine (version 1.12+ is required). Also, make sure to have golangci-lint properly set up since we use it for pre-commit hooks (for instructions on how to install it, check the [official docs](https://golangci-lint.run/usage/install/#local-installation)). - -- Fork gomatrix to your GitHub account by clicking the [Fork](https://github.com/matrix-org/gomatrix/fork) button. -- [Clone](https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork) the main repository (not your fork) to your local machine. - - - $ git clone https://github.com/matrix-org/gomatrix - $ cd gomatrix - - -- Add your fork as a remote to push your contributions.Replace - ``{username}`` with your username. - - git remote add fork https://github.com/{username}/gomatrix - -- Create a new branch to identify what feature you are working on. - - $ git fetch origin - $ git checkout -b your-branch-name origin/master - - -- Make your changes, including tests that cover any code changes you make, and run them as described below. - -- Execute pre-commit hooks by running - - /hooks/pre-commit - -- Push your changes to your fork and [create a pull request](https://help.github.com/en/articles/creating-a-pull-request) describing your changes. - - $ git push --set-upstream fork your-branch-name - -- Finally, create a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) - -## How to run tests - -You can run the test suite and example code with `$ go test -v` - -# Running Coverage - -To run coverage, first generate the coverage report using `go test` - - go test -v -cover -coverprofile=coverage.out - -You can now show the generated report as a html page with `go tool` - - go tool cover -html=coverage.out diff --git a/vendor/github.com/matrix-org/gomatrix/client.go b/vendor/github.com/matrix-org/gomatrix/client.go deleted file mode 100644 index d0bb0c5e..00000000 --- a/vendor/github.com/matrix-org/gomatrix/client.go +++ /dev/null @@ -1,805 +0,0 @@ -// Package gomatrix implements the Matrix Client-Server API. -// -// Specification can be found at http://matrix.org/docs/spec/client_server/r0.2.0.html -package gomatrix - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "path" - "strconv" - "strings" - "sync" - "time" -) - -// Client represents a Matrix client. -type Client struct { - HomeserverURL *url.URL // The base homeserver URL - Prefix string // The API prefix eg '/_matrix/client/r0' - UserID string // The user ID of the client. Used for forming HTTP paths which use the client's user ID. - AccessToken string // The access_token for the client. - Client *http.Client // The underlying HTTP client which will be used to make HTTP requests. - Syncer Syncer // The thing which can process /sync responses - Store Storer // The thing which can store rooms/tokens/ids - - // The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty, - // no user_id parameter will be sent. - // See http://matrix.org/docs/spec/application_service/unstable.html#identity-assertion - AppServiceUserID string - - syncingMutex sync.Mutex // protects syncingID - syncingID uint32 // Identifies the current Sync. Only one Sync can be active at any given time. -} - -// HTTPError An HTTP Error response, which may wrap an underlying native Go Error. -type HTTPError struct { - Contents []byte - WrappedError error - Message string - Code int -} - -func (e HTTPError) Error() string { - var wrappedErrMsg string - if e.WrappedError != nil { - wrappedErrMsg = e.WrappedError.Error() - } - return fmt.Sprintf("contents=%v msg=%s code=%d wrapped=%s", e.Contents, e.Message, e.Code, wrappedErrMsg) -} - -// BuildURL builds a URL with the Client's homeserver/prefix set already. -func (cli *Client) BuildURL(urlPath ...string) string { - ps := append([]string{cli.Prefix}, urlPath...) - return cli.BuildBaseURL(ps...) -} - -// BuildBaseURL builds a URL with the Client's homeserver set already. You must -// supply the prefix in the path. -func (cli *Client) BuildBaseURL(urlPath ...string) string { - // copy the URL. Purposefully ignore error as the input is from a valid URL already - hsURL, _ := url.Parse(cli.HomeserverURL.String()) - parts := []string{hsURL.Path} - parts = append(parts, urlPath...) - hsURL.Path = path.Join(parts...) - // Manually add the trailing slash back to the end of the path if it's explicitly needed - if strings.HasSuffix(urlPath[len(urlPath)-1], "/") { - hsURL.Path = hsURL.Path + "/" - } - query := hsURL.Query() - if cli.AppServiceUserID != "" { - query.Set("user_id", cli.AppServiceUserID) - } - hsURL.RawQuery = query.Encode() - return hsURL.String() -} - -// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix set already. -func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string { - u, _ := url.Parse(cli.BuildURL(urlPath...)) - q := u.Query() - for k, v := range urlQuery { - q.Set(k, v) - } - u.RawQuery = q.Encode() - return u.String() -} - -// SetCredentials sets the user ID and access token on this client instance. -func (cli *Client) SetCredentials(userID, accessToken string) { - cli.AccessToken = accessToken - cli.UserID = userID -} - -// ClearCredentials removes the user ID and access token on this client instance. -func (cli *Client) ClearCredentials() { - cli.AccessToken = "" - cli.UserID = "" -} - -// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the -// error will be nil. -// -// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine. -// Fatal sync errors can be caused by: -// - The failure to create a filter. -// - Client.Syncer.OnFailedSync returning an error in response to a failed sync. -// - Client.Syncer.ProcessResponse returning an error. -// If you wish to continue retrying in spite of these fatal errors, call Sync() again. -func (cli *Client) Sync() error { - // Mark the client as syncing. - // We will keep syncing until the syncing state changes. Either because - // Sync is called or StopSync is called. - syncingID := cli.incrementSyncingID() - nextBatch := cli.Store.LoadNextBatch(cli.UserID) - filterID := cli.Store.LoadFilterID(cli.UserID) - if filterID == "" { - filterJSON := cli.Syncer.GetFilterJSON(cli.UserID) - resFilter, err := cli.CreateFilter(filterJSON) - if err != nil { - return err - } - filterID = resFilter.FilterID - cli.Store.SaveFilterID(cli.UserID, filterID) - } - - for { - resSync, err := cli.SyncRequest(30000, nextBatch, filterID, false, "") - if err != nil { - duration, err2 := cli.Syncer.OnFailedSync(resSync, err) - if err2 != nil { - return err2 - } - time.Sleep(duration) - continue - } - - // Check that the syncing state hasn't changed - // Either because we've stopped syncing or another sync has been started. - // We discard the response from our sync. - if cli.getSyncingID() != syncingID { - return nil - } - - // Save the token now *before* processing it. This means it's possible - // to not process some events, but it means that we won't get constantly stuck processing - // a malformed/buggy event which keeps making us panic. - cli.Store.SaveNextBatch(cli.UserID, resSync.NextBatch) - if err = cli.Syncer.ProcessResponse(resSync, nextBatch); err != nil { - return err - } - - nextBatch = resSync.NextBatch - } -} - -func (cli *Client) incrementSyncingID() uint32 { - cli.syncingMutex.Lock() - defer cli.syncingMutex.Unlock() - cli.syncingID++ - return cli.syncingID -} - -func (cli *Client) getSyncingID() uint32 { - cli.syncingMutex.Lock() - defer cli.syncingMutex.Unlock() - return cli.syncingID -} - -// StopSync stops the ongoing sync started by Sync. -func (cli *Client) StopSync() { - // Advance the syncing state so that any running Syncs will terminate. - cli.incrementSyncingID() -} - -// MakeRequest makes a JSON HTTP request to the given URL. -// The response body will be stream decoded into an interface. This will automatically stop if the response -// body is nil. -// -// Returns an error if the response is not 2xx along with the HTTP body bytes if it got that far. This error is -// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a -// RespError as the WrappedError, if the HTTP body could be decoded as a RespError. -func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) error { - var req *http.Request - var err error - if reqBody != nil { - buf := new(bytes.Buffer) - if err := json.NewEncoder(buf).Encode(reqBody); err != nil { - return err - } - req, err = http.NewRequest(method, httpURL, buf) - } else { - req, err = http.NewRequest(method, httpURL, nil) - } - - if err != nil { - return err - } - - req.Header.Set("Content-Type", "application/json") - - if cli.AccessToken != "" { - req.Header.Set("Authorization", "Bearer "+cli.AccessToken) - } - - res, err := cli.Client.Do(req) - if res != nil { - defer res.Body.Close() - } - if err != nil { - return err - } - if res.StatusCode/100 != 2 { // not 2xx - contents, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - - var wrap error - var respErr RespError - if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" { - wrap = respErr - } - - // If we failed to decode as RespError, don't just drop the HTTP body, include it in the - // HTTP error instead (e.g proxy errors which return HTML). - msg := "Failed to " + method + " JSON to " + req.URL.Path - if wrap == nil { - msg = msg + ": " + string(contents) - } - - return HTTPError{ - Contents: contents, - Code: res.StatusCode, - Message: msg, - WrappedError: wrap, - } - } - - if resBody != nil && res.Body != nil { - return json.NewDecoder(res.Body).Decode(&resBody) - } - - return nil -} - -// CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter -func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) { - urlPath := cli.BuildURL("user", cli.UserID, "filter") - err = cli.MakeRequest("POST", urlPath, &filter, &resp) - return -} - -// SyncRequest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync -func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bool, setPresence string) (resp *RespSync, err error) { - query := map[string]string{ - "timeout": strconv.Itoa(timeout), - } - if since != "" { - query["since"] = since - } - if filterID != "" { - query["filter"] = filterID - } - if setPresence != "" { - query["set_presence"] = setPresence - } - if fullState { - query["full_state"] = "true" - } - urlPath := cli.BuildURLWithQuery([]string{"sync"}, query) - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) { - err = cli.MakeRequest("POST", u, req, &resp) - if err != nil { - httpErr, ok := err.(HTTPError) - if !ok { // network error - return - } - if httpErr.Code == 401 { - // body should be RespUserInteractive, if it isn't, fail with the error - err = json.Unmarshal(httpErr.Contents, &uiaResp) - return - } - } - return -} - -// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register -// -// Registers with kind=user. For kind=guest, see RegisterGuest. -func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) { - u := cli.BuildURL("register") - return cli.register(u, req) -} - -// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register -// with kind=guest. -// -// For kind=user, see Register. -func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) { - query := map[string]string{ - "kind": "guest", - } - u := cli.BuildURLWithQuery([]string{"register"}, query) - return cli.register(u, req) -} - -// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth -// -// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration -// this way. If the homeserver does not, an error is returned. -// -// This does not set credentials on the client instance. See SetCredentials() instead. -// -// res, err := cli.RegisterDummy(&gomatrix.ReqRegister{ -// Username: "alice", -// Password: "wonderland", -// }) -// if err != nil { -// panic(err) -// } -// token := res.AccessToken -func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) { - res, uia, err := cli.Register(req) - if err != nil && uia == nil { - return nil, err - } - if uia != nil && uia.HasSingleStageFlow("m.login.dummy") { - req.Auth = struct { - Type string `json:"type"` - Session string `json:"session,omitempty"` - }{"m.login.dummy", uia.Session} - res, _, err = cli.Register(req) - if err != nil { - return nil, err - } - } - if res == nil { - return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?") - } - return res, nil -} - -// Login a user to the homeserver according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login -// This does not set credentials on this client instance. See SetCredentials() instead. -func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) { - urlPath := cli.BuildURL("login") - err = cli.MakeRequest("POST", urlPath, req, &resp) - return -} - -// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout -// This does not clear the credentials from the client instance. See ClearCredentials() instead. -func (cli *Client) Logout() (resp *RespLogout, err error) { - urlPath := cli.BuildURL("logout") - err = cli.MakeRequest("POST", urlPath, nil, &resp) - return -} - -// LogoutAll logs the current user out on all devices. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all -// This does not clear the credentials from the client instance. See ClearCredentails() instead. -func (cli *Client) LogoutAll() (resp *RespLogoutAll, err error) { - urlPath := cli.BuildURL("logout/all") - err = cli.MakeRequest("POST", urlPath, nil, &resp) - return -} - -// Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions -func (cli *Client) Versions() (resp *RespVersions, err error) { - urlPath := cli.BuildBaseURL("_matrix", "client", "versions") - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -// PublicRooms returns the list of public rooms on target server. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-unstable-publicrooms -func (cli *Client) PublicRooms(limit int, since string, server string) (resp *RespPublicRooms, err error) { - args := map[string]string{} - - if limit != 0 { - args["limit"] = strconv.Itoa(limit) - } - if since != "" { - args["since"] = since - } - if server != "" { - args["server"] = server - } - - urlPath := cli.BuildURLWithQuery([]string{"publicRooms"}, args) - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -// PublicRoomsFiltered returns a subset of PublicRooms filtered server side. -// See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-unstable-publicrooms -func (cli *Client) PublicRoomsFiltered(limit int, since string, server string, filter string) (resp *RespPublicRooms, err error) { - content := map[string]string{} - - if limit != 0 { - content["limit"] = strconv.Itoa(limit) - } - if since != "" { - content["since"] = since - } - if filter != "" { - content["filter"] = filter - } - - var urlPath string - if server == "" { - urlPath = cli.BuildURL("publicRooms") - } else { - urlPath = cli.BuildURLWithQuery([]string{"publicRooms"}, map[string]string{ - "server": server, - }) - } - - err = cli.MakeRequest("POST", urlPath, content, &resp) - return -} - -// JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias -// -// If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will -// be JSON encoded and used as the request body. -func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{}) (resp *RespJoinRoom, err error) { - var urlPath string - if serverName != "" { - urlPath = cli.BuildURLWithQuery([]string{"join", roomIDorAlias}, map[string]string{ - "server_name": serverName, - }) - } else { - urlPath = cli.BuildURL("join", roomIDorAlias) - } - err = cli.MakeRequest("POST", urlPath, content, &resp) - return -} - -// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname -func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) { - urlPath := cli.BuildURL("profile", mxid, "displayname") - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname -func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) { - urlPath := cli.BuildURL("profile", cli.UserID, "displayname") - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname -func (cli *Client) SetDisplayName(displayName string) (err error) { - urlPath := cli.BuildURL("profile", cli.UserID, "displayname") - s := struct { - DisplayName string `json:"displayname"` - }{displayName} - err = cli.MakeRequest("PUT", urlPath, &s, nil) - return -} - -// GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url -func (cli *Client) GetAvatarURL() (string, error) { - urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url") - s := struct { - AvatarURL string `json:"avatar_url"` - }{} - - err := cli.MakeRequest("GET", urlPath, nil, &s) - if err != nil { - return "", err - } - - return s.AvatarURL, nil -} - -// SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url -func (cli *Client) SetAvatarURL(url string) error { - urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url") - s := struct { - AvatarURL string `json:"avatar_url"` - }{url} - err := cli.MakeRequest("PUT", urlPath, &s, nil) - if err != nil { - return err - } - - return nil -} - -// GetStatus returns the status of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status -func (cli *Client) GetStatus(mxid string) (resp *RespUserStatus, err error) { - urlPath := cli.BuildURL("presence", mxid, "status") - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -// GetOwnStatus returns the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status -func (cli *Client) GetOwnStatus() (resp *RespUserStatus, err error) { - return cli.GetStatus(cli.UserID) -} - -// SetStatus sets the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-presence-userid-status -func (cli *Client) SetStatus(presence, status string) (err error) { - urlPath := cli.BuildURL("presence", cli.UserID, "status") - s := struct { - Presence string `json:"presence"` - StatusMsg string `json:"status_msg"` - }{presence, status} - err = cli.MakeRequest("PUT", urlPath, &s, nil) - return -} - -// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid -// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. -func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) { - txnID := txnID() - urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID) - err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) - return -} - -// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey -// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. -func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { - urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) - err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) - return -} - -// SendText sends an m.room.message event into the given room with a msgtype of m.text -// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text -func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{MsgType: "m.text", Body: text}) -} - -// SendFormattedText sends an m.room.message event into the given room with a msgtype of m.text, supports a subset of HTML for formatting. -// See https://matrix.org/docs/spec/client_server/r0.6.0#m-text -func (cli *Client) SendFormattedText(roomID, text, formattedText string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{MsgType: "m.text", Body: text, FormattedBody: formattedText, Format: "org.matrix.custom.html"}) -} - -// SendImage sends an m.room.message event into the given room with a msgtype of m.image -// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image -func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - ImageMessage{ - MsgType: "m.image", - Body: body, - URL: url, - }) -} - -// SendVideo sends an m.room.message event into the given room with a msgtype of m.video -// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - VideoMessage{ - MsgType: "m.video", - Body: body, - URL: url, - }) -} - -// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice -// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice -func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{MsgType: "m.notice", Body: text}) -} - -// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid -func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) { - txnID := txnID() - urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID) - err = cli.MakeRequest("PUT", urlPath, req, &resp) - return -} - -// MarkRead marks eventID in roomID as read, signifying the event, and all before it have been read. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-rooms-roomid-receipt-receipttype-eventid -func (cli *Client) MarkRead(roomID, eventID string) error { - urlPath := cli.BuildURL("rooms", roomID, "receipt", "m.read", eventID) - return cli.MakeRequest("POST", urlPath, nil, nil) -} - -// CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom -// resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{ -// Preset: "public_chat", -// }) -// fmt.Println("Room:", resp.RoomID) -func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) { - urlPath := cli.BuildURL("createRoom") - err = cli.MakeRequest("POST", urlPath, req, &resp) - return -} - -// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave -func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) { - u := cli.BuildURL("rooms", roomID, "leave") - err = cli.MakeRequest("POST", u, struct{}{}, &resp) - return -} - -// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget -func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) { - u := cli.BuildURL("rooms", roomID, "forget") - err = cli.MakeRequest("POST", u, struct{}{}, &resp) - return -} - -// InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite -func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) { - u := cli.BuildURL("rooms", roomID, "invite") - err = cli.MakeRequest("POST", u, req, &resp) - return -} - -// InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint -func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) { - u := cli.BuildURL("rooms", roomID, "invite") - err = cli.MakeRequest("POST", u, req, &resp) - return -} - -// KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick -func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) { - u := cli.BuildURL("rooms", roomID, "kick") - err = cli.MakeRequest("POST", u, req, &resp) - return -} - -// BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban -func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) { - u := cli.BuildURL("rooms", roomID, "ban") - err = cli.MakeRequest("POST", u, req, &resp) - return -} - -// UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban -func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) { - u := cli.BuildURL("rooms", roomID, "unban") - err = cli.MakeRequest("POST", u, req, &resp) - return -} - -// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid -func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) { - req := ReqTyping{Typing: typing, Timeout: timeout} - u := cli.BuildURL("rooms", roomID, "typing", cli.UserID) - err = cli.MakeRequest("PUT", u, req, &resp) - return -} - -// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with -// the HTTP response body, or return an error. -// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey -func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) { - u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) - err = cli.MakeRequest("GET", u, nil, outContent) - return -} - -// UploadLink uploads an HTTP URL and then returns an MXC URI. -func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) { - res, err := cli.Client.Get(link) - if res != nil { - defer res.Body.Close() - } - if err != nil { - return nil, err - } - return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength) -} - -// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI. -// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload -func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { - req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", contentType) - req.Header.Set("Authorization", "Bearer "+cli.AccessToken) - - req.ContentLength = contentLength - - res, err := cli.Client.Do(req) - if res != nil { - defer res.Body.Close() - } - - if err != nil { - return nil, err - } - - if res.StatusCode != 200 { - contents, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, HTTPError{ - Message: "Upload request failed - Failed to read response body: " + err.Error(), - Code: res.StatusCode, - } - } - return nil, HTTPError{ - Contents: contents, - Message: "Upload request failed: " + string(contents), - Code: res.StatusCode, - } - } - - var m RespMediaUpload - if err := json.NewDecoder(res.Body).Decode(&m); err != nil { - return nil, err - } - - return &m, nil -} - -// JoinedMembers returns a map of joined room members. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680 -// -// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes. -// This API is primarily designed for application services which may want to efficiently look up joined members in a room. -func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) { - u := cli.BuildURL("rooms", roomID, "joined_members") - err = cli.MakeRequest("GET", u, nil, &resp) - return -} - -// JoinedRooms returns a list of rooms which the client is joined to. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680 -// -// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes. -// This API is primarily designed for application services which may want to efficiently look up joined rooms. -func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) { - u := cli.BuildURL("joined_rooms") - err = cli.MakeRequest("GET", u, nil, &resp) - return -} - -// Messages returns a list of message and state events for a room. It uses -// pagination query parameters to paginate history in the room. -// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages -func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) { - query := map[string]string{ - "from": from, - "dir": string(dir), - } - if to != "" { - query["to"] = to - } - if limit != 0 { - query["limit"] = strconv.Itoa(limit) - } - - urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query) - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -// TurnServer returns turn server details and credentials for the client to use when initiating calls. -// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver -func (cli *Client) TurnServer() (resp *RespTurnServer, err error) { - urlPath := cli.BuildURL("voip", "turnServer") - err = cli.MakeRequest("GET", urlPath, nil, &resp) - return -} - -func txnID() string { - return "go" + strconv.FormatInt(time.Now().UnixNano(), 10) -} - -// NewClient creates a new Matrix Client ready for syncing -func NewClient(homeserverURL, userID, accessToken string) (*Client, error) { - hsURL, err := url.Parse(homeserverURL) - if err != nil { - return nil, err - } - // By default, use an in-memory store which will never save filter ids / next batch tokens to disk. - // The client will work with this storer: it just won't remember across restarts. - // In practice, a database backend should be used. - store := NewInMemoryStore() - cli := Client{ - AccessToken: accessToken, - HomeserverURL: hsURL, - UserID: userID, - Prefix: "/_matrix/client/r0", - Syncer: NewDefaultSyncer(userID, store), - Store: store, - } - // By default, use the default HTTP client. - cli.Client = http.DefaultClient - - return &cli, nil -} diff --git a/vendor/github.com/matrix-org/gomatrix/events.go b/vendor/github.com/matrix-org/gomatrix/events.go deleted file mode 100644 index cbc70a82..00000000 --- a/vendor/github.com/matrix-org/gomatrix/events.go +++ /dev/null @@ -1,157 +0,0 @@ -package gomatrix - -import ( - "html" - "regexp" -) - -// Event represents a single Matrix event. -type Event struct { - StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. - Sender string `json:"sender"` // The user ID of the sender of the event - Type string `json:"type"` // The event type - Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server - ID string `json:"event_id"` // The unique ID of this event - RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) - Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event - Unsigned map[string]interface{} `json:"unsigned"` // The unsigned portions of the event, such as age and prev_content - Content map[string]interface{} `json:"content"` // The JSON content of the event. - PrevContent map[string]interface{} `json:"prev_content,omitempty"` // The JSON prev_content of the event. -} - -// Body returns the value of the "body" key in the event content if it is -// present and is a string. -func (event *Event) Body() (body string, ok bool) { - value, exists := event.Content["body"] - if !exists { - return - } - body, ok = value.(string) - return -} - -// MessageType returns the value of the "msgtype" key in the event content if -// it is present and is a string. -func (event *Event) MessageType() (msgtype string, ok bool) { - value, exists := event.Content["msgtype"] - if !exists { - return - } - msgtype, ok = value.(string) - return -} - -// TextMessage is the contents of a Matrix formated message event. -type TextMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - FormattedBody string `json:"formatted_body"` - Format string `json:"format"` -} - -// ThumbnailInfo contains info about an thumbnail image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image -type ThumbnailInfo struct { - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Mimetype string `json:"mimetype,omitempty"` - Size uint `json:"size,omitempty"` -} - -// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image -type ImageInfo struct { - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Mimetype string `json:"mimetype,omitempty"` - Size uint `json:"size,omitempty"` - ThumbnailInfo ThumbnailInfo `json:"thumbnail_info,omitempty"` - ThumbnailURL string `json:"thumbnail_url,omitempty"` -} - -// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -type VideoInfo struct { - Mimetype string `json:"mimetype,omitempty"` - ThumbnailInfo ThumbnailInfo `json:"thumbnail_info"` - ThumbnailURL string `json:"thumbnail_url,omitempty"` - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Duration uint `json:"duration,omitempty"` - Size uint `json:"size,omitempty"` -} - -// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -type VideoMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info VideoInfo `json:"info"` -} - -// ImageMessage is an m.image event -type ImageMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info ImageInfo `json:"info"` -} - -// An HTMLMessage is the contents of a Matrix HTML formated message event. -type HTMLMessage struct { - Body string `json:"body"` - MsgType string `json:"msgtype"` - Format string `json:"format"` - FormattedBody string `json:"formatted_body"` -} - -// FileInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file -type FileInfo struct { - Mimetype string `json:"mimetype,omitempty"` - Size uint `json:"size,omitempty"` //filesize in bytes -} - -// FileMessage is an m.file event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file -type FileMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Filename string `json:"filename"` - Info FileInfo `json:"info,omitempty"` - ThumbnailURL string `json:"thumbnail_url,omitempty"` - ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"` -} - -// LocationMessage is an m.location event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location -type LocationMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - GeoURI string `json:"geo_uri"` - ThumbnailURL string `json:"thumbnail_url,omitempty"` - ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"` -} - -// AudioInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio -type AudioInfo struct { - Mimetype string `json:"mimetype,omitempty"` - Size uint `json:"size,omitempty"` //filesize in bytes - Duration uint `json:"duration,omitempty"` //audio duration in ms -} - -// AudioMessage is an m.audio event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio -type AudioMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info AudioInfo `json:"info,omitempty"` -} - -var htmlRegex = regexp.MustCompile("<[^<]+?>") - -// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition -// to the provided HTML. -func GetHTMLMessage(msgtype, htmlText string) HTMLMessage { - return HTMLMessage{ - Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")), - MsgType: msgtype, - Format: "org.matrix.custom.html", - FormattedBody: htmlText, - } -} diff --git a/vendor/github.com/matrix-org/gomatrix/filter.go b/vendor/github.com/matrix-org/gomatrix/filter.go deleted file mode 100644 index 2a0c37fa..00000000 --- a/vendor/github.com/matrix-org/gomatrix/filter.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 Jan Christian Grünhage -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gomatrix - -import "errors" - -//Filter is used by clients to specify how the server should filter responses to e.g. sync requests -//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering -type Filter struct { - AccountData FilterPart `json:"account_data,omitempty"` - EventFields []string `json:"event_fields,omitempty"` - EventFormat string `json:"event_format,omitempty"` - Presence FilterPart `json:"presence,omitempty"` - Room RoomFilter `json:"room,omitempty"` -} - -// RoomFilter is used to define filtering rules for room events -type RoomFilter struct { - AccountData FilterPart `json:"account_data,omitempty"` - Ephemeral FilterPart `json:"ephemeral,omitempty"` - IncludeLeave bool `json:"include_leave,omitempty"` - NotRooms []string `json:"not_rooms,omitempty"` - Rooms []string `json:"rooms,omitempty"` - State FilterPart `json:"state,omitempty"` - Timeline FilterPart `json:"timeline,omitempty"` -} - -// FilterPart is used to define filtering rules for specific categories of events -type FilterPart struct { - NotRooms []string `json:"not_rooms,omitempty"` - Rooms []string `json:"rooms,omitempty"` - Limit int `json:"limit,omitempty"` - NotSenders []string `json:"not_senders,omitempty"` - NotTypes []string `json:"not_types,omitempty"` - Senders []string `json:"senders,omitempty"` - Types []string `json:"types,omitempty"` - ContainsURL *bool `json:"contains_url,omitempty"` -} - -// Validate checks if the filter contains valid property values -func (filter *Filter) Validate() error { - if filter.EventFormat != "client" && filter.EventFormat != "federation" { - return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]") - } - return nil -} - -// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request -func DefaultFilter() Filter { - return Filter{ - AccountData: DefaultFilterPart(), - EventFields: nil, - EventFormat: "client", - Presence: DefaultFilterPart(), - Room: RoomFilter{ - AccountData: DefaultFilterPart(), - Ephemeral: DefaultFilterPart(), - IncludeLeave: false, - NotRooms: nil, - Rooms: nil, - State: DefaultFilterPart(), - Timeline: DefaultFilterPart(), - }, - } -} - -// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request -func DefaultFilterPart() FilterPart { - return FilterPart{ - NotRooms: nil, - Rooms: nil, - Limit: 20, - NotSenders: nil, - NotTypes: nil, - Senders: nil, - Types: nil, - } -} diff --git a/vendor/github.com/matrix-org/gomatrix/identifier.go b/vendor/github.com/matrix-org/gomatrix/identifier.go deleted file mode 100644 index 4a61d080..00000000 --- a/vendor/github.com/matrix-org/gomatrix/identifier.go +++ /dev/null @@ -1,69 +0,0 @@ -package gomatrix - -// Identifier is the interface for https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types -type Identifier interface { - // Returns the identifier type - // https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types - Type() string -} - -// UserIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#matrix-user-id -type UserIdentifier struct { - IDType string `json:"type"` // Set by NewUserIdentifer - User string `json:"user"` -} - -// Type implements the Identifier interface -func (i UserIdentifier) Type() string { - return "m.id.user" -} - -// NewUserIdentifier creates a new UserIdentifier with IDType set to "m.id.user" -func NewUserIdentifier(user string) UserIdentifier { - return UserIdentifier{ - IDType: "m.id.user", - User: user, - } -} - -// ThirdpartyIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#third-party-id -type ThirdpartyIdentifier struct { - IDType string `json:"type"` // Set by NewThirdpartyIdentifier - Medium string `json:"medium"` - Address string `json:"address"` -} - -// Type implements the Identifier interface -func (i ThirdpartyIdentifier) Type() string { - return "m.id.thirdparty" -} - -// NewThirdpartyIdentifier creates a new UserIdentifier with IDType set to "m.id.user" -func NewThirdpartyIdentifier(medium, address string) ThirdpartyIdentifier { - return ThirdpartyIdentifier{ - IDType: "m.id.thirdparty", - Medium: medium, - Address: address, - } -} - -// PhoneIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#phone-number -type PhoneIdentifier struct { - IDType string `json:"type"` // Set by NewPhoneIdentifier - Country string `json:"country"` - Phone string `json:"phone"` -} - -// Type implements the Identifier interface -func (i PhoneIdentifier) Type() string { - return "m.id.phone" -} - -// NewPhoneIdentifier creates a new UserIdentifier with IDType set to "m.id.user" -func NewPhoneIdentifier(country, phone string) PhoneIdentifier { - return PhoneIdentifier{ - IDType: "m.id.phone", - Country: country, - Phone: phone, - } -} diff --git a/vendor/github.com/matrix-org/gomatrix/requests.go b/vendor/github.com/matrix-org/gomatrix/requests.go deleted file mode 100644 index 31c426d4..00000000 --- a/vendor/github.com/matrix-org/gomatrix/requests.go +++ /dev/null @@ -1,79 +0,0 @@ -package gomatrix - -// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register -type ReqRegister struct { - Username string `json:"username,omitempty"` - BindEmail bool `json:"bind_email,omitempty"` - Password string `json:"password,omitempty"` - DeviceID string `json:"device_id,omitempty"` - InitialDeviceDisplayName string `json:"initial_device_display_name"` - Auth interface{} `json:"auth,omitempty"` -} - -// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login -type ReqLogin struct { - Type string `json:"type"` - Identifier Identifier `json:"identifier,omitempty"` - Password string `json:"password,omitempty"` - Medium string `json:"medium,omitempty"` - User string `json:"user,omitempty"` - Address string `json:"address,omitempty"` - Token string `json:"token,omitempty"` - DeviceID string `json:"device_id,omitempty"` - InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"` -} - -// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom -type ReqCreateRoom struct { - Visibility string `json:"visibility,omitempty"` - RoomAliasName string `json:"room_alias_name,omitempty"` - Name string `json:"name,omitempty"` - Topic string `json:"topic,omitempty"` - Invite []string `json:"invite,omitempty"` - Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"` - CreationContent map[string]interface{} `json:"creation_content,omitempty"` - InitialState []Event `json:"initial_state,omitempty"` - Preset string `json:"preset,omitempty"` - IsDirect bool `json:"is_direct,omitempty"` -} - -// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid -type ReqRedact struct { - Reason string `json:"reason,omitempty"` -} - -// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57 -// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom -type ReqInvite3PID struct { - IDServer string `json:"id_server"` - Medium string `json:"medium"` - Address string `json:"address"` -} - -// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite -type ReqInviteUser struct { - UserID string `json:"user_id"` -} - -// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick -type ReqKickUser struct { - Reason string `json:"reason,omitempty"` - UserID string `json:"user_id"` -} - -// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban -type ReqBanUser struct { - Reason string `json:"reason,omitempty"` - UserID string `json:"user_id"` -} - -// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban -type ReqUnbanUser struct { - UserID string `json:"user_id"` -} - -// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid -type ReqTyping struct { - Typing bool `json:"typing"` - Timeout int64 `json:"timeout"` -} diff --git a/vendor/github.com/matrix-org/gomatrix/responses.go b/vendor/github.com/matrix-org/gomatrix/responses.go deleted file mode 100644 index f488e69e..00000000 --- a/vendor/github.com/matrix-org/gomatrix/responses.go +++ /dev/null @@ -1,210 +0,0 @@ -package gomatrix - -// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface. -// See http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards -type RespError struct { - ErrCode string `json:"errcode"` - Err string `json:"error"` -} - -// Error returns the errcode and error message. -func (e RespError) Error() string { - return e.ErrCode + ": " + e.Err -} - -// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter -type RespCreateFilter struct { - FilterID string `json:"filter_id"` -} - -// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions -type RespVersions struct { - Versions []string `json:"versions"` -} - -// RespPublicRooms is the JSON response for http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms -type RespPublicRooms struct { - TotalRoomCountEstimate int `json:"total_room_count_estimate"` - PrevBatch string `json:"prev_batch"` - NextBatch string `json:"next_batch"` - Chunk []PublicRoom `json:"chunk"` -} - -// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join -type RespJoinRoom struct { - RoomID string `json:"room_id"` -} - -// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave -type RespLeaveRoom struct{} - -// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget -type RespForgetRoom struct{} - -// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite -type RespInviteUser struct{} - -// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick -type RespKickUser struct{} - -// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban -type RespBanUser struct{} - -// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban -type RespUnbanUser struct{} - -// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid -type RespTyping struct{} - -// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680 -type RespJoinedRooms struct { - JoinedRooms []string `json:"joined_rooms"` -} - -// RespJoinedMembers is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680 -type RespJoinedMembers struct { - Joined map[string]struct { - DisplayName *string `json:"display_name"` - AvatarURL *string `json:"avatar_url"` - } `json:"joined"` -} - -// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages -type RespMessages struct { - Start string `json:"start"` - Chunk []Event `json:"chunk"` - End string `json:"end"` -} - -// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid -type RespSendEvent struct { - EventID string `json:"event_id"` -} - -// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload -type RespMediaUpload struct { - ContentURI string `json:"content_uri"` -} - -// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api -type RespUserInteractive struct { - Flows []struct { - Stages []string `json:"stages"` - } `json:"flows"` - Params map[string]interface{} `json:"params"` - Session string `json:"session"` - Completed []string `json:"completed"` - ErrCode string `json:"errcode"` - Error string `json:"error"` -} - -// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName. -func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool { - for _, f := range r.Flows { - if len(f.Stages) == 1 && f.Stages[0] == stageName { - return true - } - } - return false -} - -// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname -type RespUserDisplayName struct { - DisplayName string `json:"displayname"` -} - -// RespUserStatus is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status -type RespUserStatus struct { - Presence string `json:"presence"` - StatusMsg string `json:"status_msg"` - LastActiveAgo int `json:"last_active_ago"` - CurrentlyActive bool `json:"currently_active"` -} - -// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register -type RespRegister struct { - AccessToken string `json:"access_token"` - DeviceID string `json:"device_id"` - HomeServer string `json:"home_server"` - RefreshToken string `json:"refresh_token"` - UserID string `json:"user_id"` -} - -// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login -type RespLogin struct { - AccessToken string `json:"access_token"` - DeviceID string `json:"device_id"` - HomeServer string `json:"home_server"` - UserID string `json:"user_id"` - WellKnown DiscoveryInformation `json:"well_known"` -} - -// DiscoveryInformation is the JSON Response for https://matrix.org/docs/spec/client_server/r0.6.0#get-well-known-matrix-client and a part of the JSON Response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login -type DiscoveryInformation struct { - Homeserver struct { - BaseURL string `json:"base_url"` - } `json:"m.homeserver"` - IdentityServer struct { - BaseURL string `json:"base_url"` - } `json:"m.identitiy_server"` -} - -// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout -type RespLogout struct{} - -// RespLogoutAll is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all -type RespLogoutAll struct{} - -// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom -type RespCreateRoom struct { - RoomID string `json:"room_id"` -} - -// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync -type RespSync struct { - NextBatch string `json:"next_batch"` - AccountData struct { - Events []Event `json:"events"` - } `json:"account_data"` - Presence struct { - Events []Event `json:"events"` - } `json:"presence"` - Rooms struct { - Leave map[string]struct { - State struct { - Events []Event `json:"events"` - } `json:"state"` - Timeline struct { - Events []Event `json:"events"` - Limited bool `json:"limited"` - PrevBatch string `json:"prev_batch"` - } `json:"timeline"` - } `json:"leave"` - Join map[string]struct { - State struct { - Events []Event `json:"events"` - } `json:"state"` - Timeline struct { - Events []Event `json:"events"` - Limited bool `json:"limited"` - PrevBatch string `json:"prev_batch"` - } `json:"timeline"` - Ephemeral struct { - Events []Event `json:"events"` - } `json:"ephemeral"` - } `json:"join"` - Invite map[string]struct { - State struct { - Events []Event - } `json:"invite_state"` - } `json:"invite"` - } `json:"rooms"` -} - -// RespTurnServer is the JSON response from a Turn Server -type RespTurnServer struct { - Username string `json:"username"` - Password string `json:"password"` - TTL int `json:"ttl"` - URIs []string `json:"uris"` -} diff --git a/vendor/github.com/matrix-org/gomatrix/room.go b/vendor/github.com/matrix-org/gomatrix/room.go deleted file mode 100644 index 364deab2..00000000 --- a/vendor/github.com/matrix-org/gomatrix/room.go +++ /dev/null @@ -1,63 +0,0 @@ -package gomatrix - -// Room represents a single Matrix room. -type Room struct { - ID string - State map[string]map[string]*Event -} - -// PublicRoom represents the information about a public room obtainable from the room directory -type PublicRoom struct { - CanonicalAlias string `json:"canonical_alias"` - Name string `json:"name"` - WorldReadable bool `json:"world_readable"` - Topic string `json:"topic"` - NumJoinedMembers int `json:"num_joined_members"` - AvatarURL string `json:"avatar_url"` - RoomID string `json:"room_id"` - GuestCanJoin bool `json:"guest_can_join"` - Aliases []string `json:"aliases"` -} - -// UpdateState updates the room's current state with the given Event. This will clobber events based -// on the type/state_key combination. -func (room Room) UpdateState(event *Event) { - _, exists := room.State[event.Type] - if !exists { - room.State[event.Type] = make(map[string]*Event) - } - room.State[event.Type][*event.StateKey] = event -} - -// GetStateEvent returns the state event for the given type/state_key combo, or nil. -func (room Room) GetStateEvent(eventType string, stateKey string) *Event { - stateEventMap := room.State[eventType] - event := stateEventMap[stateKey] - return event -} - -// GetMembershipState returns the membership state of the given user ID in this room. If there is -// no entry for this member, 'leave' is returned for consistency with left users. -func (room Room) GetMembershipState(userID string) string { - state := "leave" - event := room.GetStateEvent("m.room.member", userID) - if event != nil { - membershipState, found := event.Content["membership"] - if found { - mState, isString := membershipState.(string) - if isString { - state = mState - } - } - } - return state -} - -// NewRoom creates a new Room with the given ID -func NewRoom(roomID string) *Room { - // Init the State map and return a pointer to the Room - return &Room{ - ID: roomID, - State: make(map[string]map[string]*Event), - } -} diff --git a/vendor/github.com/matrix-org/gomatrix/store.go b/vendor/github.com/matrix-org/gomatrix/store.go deleted file mode 100644 index 6dc687e5..00000000 --- a/vendor/github.com/matrix-org/gomatrix/store.go +++ /dev/null @@ -1,65 +0,0 @@ -package gomatrix - -// Storer is an interface which must be satisfied to store client data. -// -// You can either write a struct which persists this data to disk, or you can use the -// provided "InMemoryStore" which just keeps data around in-memory which is lost on -// restarts. -type Storer interface { - SaveFilterID(userID, filterID string) - LoadFilterID(userID string) string - SaveNextBatch(userID, nextBatchToken string) - LoadNextBatch(userID string) string - SaveRoom(room *Room) - LoadRoom(roomID string) *Room -} - -// InMemoryStore implements the Storer interface. -// -// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs -// or next batch tokens on any goroutine other than the syncing goroutine: the one -// which called Client.Sync(). -type InMemoryStore struct { - Filters map[string]string - NextBatch map[string]string - Rooms map[string]*Room -} - -// SaveFilterID to memory. -func (s *InMemoryStore) SaveFilterID(userID, filterID string) { - s.Filters[userID] = filterID -} - -// LoadFilterID from memory. -func (s *InMemoryStore) LoadFilterID(userID string) string { - return s.Filters[userID] -} - -// SaveNextBatch to memory. -func (s *InMemoryStore) SaveNextBatch(userID, nextBatchToken string) { - s.NextBatch[userID] = nextBatchToken -} - -// LoadNextBatch from memory. -func (s *InMemoryStore) LoadNextBatch(userID string) string { - return s.NextBatch[userID] -} - -// SaveRoom to memory. -func (s *InMemoryStore) SaveRoom(room *Room) { - s.Rooms[room.ID] = room -} - -// LoadRoom from memory. -func (s *InMemoryStore) LoadRoom(roomID string) *Room { - return s.Rooms[roomID] -} - -// NewInMemoryStore constructs a new InMemoryStore. -func NewInMemoryStore() *InMemoryStore { - return &InMemoryStore{ - Filters: make(map[string]string), - NextBatch: make(map[string]string), - Rooms: make(map[string]*Room), - } -} diff --git a/vendor/github.com/matrix-org/gomatrix/sync.go b/vendor/github.com/matrix-org/gomatrix/sync.go deleted file mode 100644 index ac326c3a..00000000 --- a/vendor/github.com/matrix-org/gomatrix/sync.go +++ /dev/null @@ -1,168 +0,0 @@ -package gomatrix - -import ( - "encoding/json" - "fmt" - "runtime/debug" - "time" -) - -// Syncer represents an interface that must be satisfied in order to do /sync requests on a client. -type Syncer interface { - // Process the /sync response. The since parameter is the since= value that was used to produce the response. - // This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped - // permanently. - ProcessResponse(resp *RespSync, since string) error - // OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently. - OnFailedSync(res *RespSync, err error) (time.Duration, error) - // GetFilterJSON for the given user ID. NOT the filter ID. - GetFilterJSON(userID string) json.RawMessage -} - -// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively -// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer -// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information. -type DefaultSyncer struct { - UserID string - Store Storer - listeners map[string][]OnEventListener // event type to listeners array -} - -// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events. -type OnEventListener func(*Event) - -// NewDefaultSyncer returns an instantiated DefaultSyncer -func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer { - return &DefaultSyncer{ - UserID: userID, - Store: store, - listeners: make(map[string][]OnEventListener), - } -} - -// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of -// unrepeating events. Returns a fatal error if a listener panics. -func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) { - if !s.shouldProcessResponse(res, since) { - return - } - - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack()) - } - }() - - for roomID, roomData := range res.Rooms.Join { - room := s.getOrCreateRoom(roomID) - for _, event := range roomData.State.Events { - event.RoomID = roomID - room.UpdateState(&event) - s.notifyListeners(&event) - } - for _, event := range roomData.Timeline.Events { - event.RoomID = roomID - s.notifyListeners(&event) - } - for _, event := range roomData.Ephemeral.Events { - event.RoomID = roomID - s.notifyListeners(&event) - } - } - for roomID, roomData := range res.Rooms.Invite { - room := s.getOrCreateRoom(roomID) - for _, event := range roomData.State.Events { - event.RoomID = roomID - room.UpdateState(&event) - s.notifyListeners(&event) - } - } - for roomID, roomData := range res.Rooms.Leave { - room := s.getOrCreateRoom(roomID) - for _, event := range roomData.Timeline.Events { - if event.StateKey != nil { - event.RoomID = roomID - room.UpdateState(&event) - s.notifyListeners(&event) - } - } - } - return -} - -// OnEventType allows callers to be notified when there are new events for the given event type. -// There are no duplicate checks. -func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) { - _, exists := s.listeners[eventType] - if !exists { - s.listeners[eventType] = []OnEventListener{} - } - s.listeners[eventType] = append(s.listeners[eventType], callback) -} - -// shouldProcessResponse returns true if the response should be processed. May modify the response to remove -// stuff that shouldn't be processed. -func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool { - if since == "" { - return false - } - // This is a horrible hack because /sync will return the most recent messages for a room - // as soon as you /join it. We do NOT want to process those events in that particular room - // because they may have already been processed (if you toggle the bot in/out of the room). - // - // Work around this by inspecting each room's timeline and seeing if an m.room.member event for us - // exists and is "join" and then discard processing that room entirely if so. - // TODO: We probably want to process messages from after the last join event in the timeline. - for roomID, roomData := range resp.Rooms.Join { - for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { - e := roomData.Timeline.Events[i] - if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID { - m := e.Content["membership"] - mship, ok := m.(string) - if !ok { - continue - } - if mship == "join" { - _, ok := resp.Rooms.Join[roomID] - if !ok { - continue - } - delete(resp.Rooms.Join, roomID) // don't re-process messages - delete(resp.Rooms.Invite, roomID) // don't re-process invites - break - } - } - } - } - return true -} - -// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse() -func (s *DefaultSyncer) getOrCreateRoom(roomID string) *Room { - room := s.Store.LoadRoom(roomID) - if room == nil { // create a new Room - room = NewRoom(roomID) - s.Store.SaveRoom(room) - } - return room -} - -func (s *DefaultSyncer) notifyListeners(event *Event) { - listeners, exists := s.listeners[event.Type] - if !exists { - return - } - for _, fn := range listeners { - fn(event) - } -} - -// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error. -func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) { - return 10 * time.Second, nil -} - -// GetFilterJSON returns a filter with a timeline limit of 50. -func (s *DefaultSyncer) GetFilterJSON(userID string) json.RawMessage { - return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`) -} diff --git a/vendor/github.com/matrix-org/gomatrix/tags.go b/vendor/github.com/matrix-org/gomatrix/tags.go deleted file mode 100644 index 956fb11e..00000000 --- a/vendor/github.com/matrix-org/gomatrix/tags.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 Sumukha PK -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gomatrix - -// TagContent contains the data for an m.tag message type -// https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag -type TagContent struct { - Tags map[string]TagProperties `json:"tags"` -} - -// TagProperties contains the properties of a Tag -type TagProperties struct { - Order float32 `json:"order,omitempty"` // Empty values must be neglected -} diff --git a/vendor/github.com/matrix-org/gomatrix/userids.go b/vendor/github.com/matrix-org/gomatrix/userids.go deleted file mode 100644 index 70002c5b..00000000 --- a/vendor/github.com/matrix-org/gomatrix/userids.go +++ /dev/null @@ -1,130 +0,0 @@ -package gomatrix - -import ( - "bytes" - "encoding/hex" - "fmt" - "strings" -) - -const lowerhex = "0123456789abcdef" - -// encode the given byte using quoted-printable encoding (e.g "=2f") -// and writes it to the buffer -// See https://golang.org/src/mime/quotedprintable/writer.go -func encode(buf *bytes.Buffer, b byte) { - buf.WriteByte('=') - buf.WriteByte(lowerhex[b>>4]) - buf.WriteByte(lowerhex[b&0x0f]) -} - -// escape the given alpha character and writes it to the buffer -func escape(buf *bytes.Buffer, b byte) { - buf.WriteByte('_') - if b == '_' { - buf.WriteByte('_') // another _ - } else { - buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z - } -} - -func shouldEncode(b byte) bool { - return b != '-' && b != '.' && b != '_' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z') -} - -func shouldEscape(b byte) bool { - return (b >= 'A' && b <= 'Z') || b == '_' -} - -func isValidByte(b byte) bool { - return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-' -} - -func isValidEscapedChar(b byte) bool { - return b == '_' || (b >= 'a' && b <= 'z') -} - -// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form. -// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets -// -// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z -// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges -// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs) -// and converted to lower-case hex with a leading "=". For example: -// Alph@Bet_50up => _alph=40_bet=5f50up -func EncodeUserLocalpart(str string) string { - strBytes := []byte(str) - var outputBuffer bytes.Buffer - for _, b := range strBytes { - if shouldEncode(b) { - encode(&outputBuffer, b) - } else if shouldEscape(b) { - escape(&outputBuffer, b) - } else { - outputBuffer.WriteByte(b) - } - } - return outputBuffer.String() -} - -// DecodeUserLocalpart decodes the given string back into the original input string. -// Returns an error if the given string is not a valid user ID localpart encoding. -// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets -// -// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For -// example: -// _alph=40_bet=5f50up => Alph@Bet_50up -// Returns an error if the input string contains characters outside the -// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has -// an invalid _ escaped byte (e.g. "_5"). -func DecodeUserLocalpart(str string) (string, error) { - strBytes := []byte(str) - var outputBuffer bytes.Buffer - for i := 0; i < len(strBytes); i++ { - b := strBytes[i] - if !isValidByte(b) { - return "", fmt.Errorf("Byte pos %d: Invalid byte", i) - } - - if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _ - if i+1 >= len(strBytes) { - return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i) - } - if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping - return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i) - } - if strBytes[i+1] == '_' { - outputBuffer.WriteByte('_') - } else { - outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z - } - i++ // skip next byte since we just handled it - } else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8 - if i+2 >= len(strBytes) { - return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i) - } - dst := make([]byte, 1) - _, err := hex.Decode(dst, strBytes[i+1:i+3]) - if err != nil { - return "", err - } - outputBuffer.WriteByte(dst[0]) - i += 2 // skip next 2 bytes since we just handled it - } else { // pass through - outputBuffer.WriteByte(b) - } - } - return outputBuffer.String(), nil -} - -// ExtractUserLocalpart extracts the localpart portion of a user ID. -// See http://matrix.org/docs/spec/intro.html#user-identifiers -func ExtractUserLocalpart(userID string) (string, error) { - if len(userID) == 0 || userID[0] != '@' { - return "", fmt.Errorf("%s is not a valid user id", userID) - } - return strings.TrimPrefix( - strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ] - "@", // remove "@" prefix - ), nil -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go deleted file mode 100644 index 416d1bbb..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_appengine.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build appengine -// +build appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/mattn/go-isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go index 766d9460..c1a78aa9 100644 --- a/vendor/github.com/mattn/go-colorable/colorable_others.go +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -1,5 +1,5 @@ -//go:build !windows && !appengine -// +build !windows,!appengine +//go:build !windows || appengine +// +build !windows appengine package colorable diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go index 1846ad5a..2df7b859 100644 --- a/vendor/github.com/mattn/go-colorable/colorable_windows.go +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -11,7 +11,7 @@ import ( "strconv" "strings" "sync" - "syscall" + syscall "golang.org/x/sys/windows" "unsafe" "github.com/mattn/go-isatty" @@ -73,7 +73,7 @@ type consoleCursorInfo struct { } var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") + kernel32 = syscall.NewLazySystemDLL("kernel32.dll") procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") @@ -87,8 +87,8 @@ var ( procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") ) -// Writer provides colorable Writer to the console -type Writer struct { +// writer provides colorable Writer to the console +type writer struct { out io.Writer handle syscall.Handle althandle syscall.Handle @@ -98,7 +98,7 @@ type Writer struct { mutex sync.Mutex } -// NewColorable returns new instance of Writer which handles escape sequence from File. +// NewColorable returns new instance of writer which handles escape sequence from File. func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") @@ -112,17 +112,17 @@ func NewColorable(file *os.File) io.Writer { var csbi consoleScreenBufferInfo handle := syscall.Handle(file.Fd()) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + return &writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} } return file } -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +// NewColorableStdout returns new instance of writer which handles escape sequence for stdout. func NewColorableStdout() io.Writer { return NewColorable(os.Stdout) } -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +// NewColorableStderr returns new instance of writer which handles escape sequence for stderr. func NewColorableStderr() io.Writer { return NewColorable(os.Stderr) } @@ -434,7 +434,7 @@ func atoiWithDefault(s string, def int) (int, error) { } // Write writes data on console -func (w *Writer) Write(data []byte) (n int, err error) { +func (w *writer) Write(data []byte) (n int, err error) { w.mutex.Lock() defer w.mutex.Unlock() var csbi consoleScreenBufferInfo @@ -560,7 +560,7 @@ loop: } procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'E': - n, err = strconv.Atoi(buf.String()) + n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } @@ -569,7 +569,7 @@ loop: csbi.cursorPosition.y += short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'F': - n, err = strconv.Atoi(buf.String()) + n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go index d569c0c9..d0ea68f4 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -1,6 +1,7 @@ -//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine +//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine && !tinygo // +build darwin freebsd openbsd netbsd dragonfly hurd // +build !appengine +// +build !tinygo package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go index 31503226..7402e061 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -1,5 +1,6 @@ -//go:build appengine || js || nacl || wasm -// +build appengine js nacl wasm +//go:build (appengine || js || nacl || tinygo || wasm) && !windows +// +build appengine js nacl tinygo wasm +// +build !windows package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go index 67787657..0337d8cf 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -1,6 +1,7 @@ -//go:build (linux || aix || zos) && !appengine +//go:build (linux || aix || zos) && !appengine && !tinygo // +build linux aix zos // +build !appengine +// +build !tinygo package isatty diff --git a/vendor/github.com/tidwall/gjson/LICENSE b/vendor/github.com/tidwall/gjson/LICENSE new file mode 100644 index 00000000..58f5819a --- /dev/null +++ b/vendor/github.com/tidwall/gjson/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md new file mode 100644 index 00000000..387766d5 --- /dev/null +++ b/vendor/github.com/tidwall/gjson/README.md @@ -0,0 +1,492 @@ +

+ + + + GJSON + +
+GoDoc +GJSON Playground +GJSON Syntax + +

+ +

get json values quickly

+ +GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document. +It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing json lines](#json-lines). + +Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool. + +This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md). + +GJSON is also available for [Python](https://github.com/volans-/gjson-py) and [Rust](https://github.com/tidwall/gjson.rs) + +Getting Started +=============== + +## Installing + +To start using GJSON, install Go and run `go get`: + +```sh +$ go get -u github.com/tidwall/gjson +``` + +This will retrieve the library. + +## Get a value +Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately. + +```go +package main + +import "github.com/tidwall/gjson" + +const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}` + +func main() { + value := gjson.Get(json, "name.last") + println(value.String()) +} +``` + +This will print: + +``` +Prichard +``` +*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.* + +## Path Syntax + +Below is a quick overview of the path syntax, for more complete information please +check out [GJSON Syntax](SYNTAX.md). + +A path is a series of keys separated by a dot. +A key may contain special wildcard characters '\*' and '?'. +To access an array value use the index as the key. +To get the number of elements in an array or to access a child path, use the '#' character. +The dot and wildcard characters can be escaped with '\\'. + +```json +{ + "name": {"first": "Tom", "last": "Anderson"}, + "age":37, + "children": ["Sara","Alex","Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, + {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, + {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} + ] +} +``` +``` +"name.last" >> "Anderson" +"age" >> 37 +"children" >> ["Sara","Alex","Jack"] +"children.#" >> 3 +"children.1" >> "Alex" +"child*.2" >> "Jack" +"c?ildren.0" >> "Sara" +"fav\.movie" >> "Deer Hunter" +"friends.#.first" >> ["Dale","Roger","Jane"] +"friends.1.last" >> "Craig" +``` + +You can also query an array for the first match by using `#(...)`, or find all +matches with `#(...)#`. Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` +comparison operators and the simple pattern matching `%` (like) and `!%` +(not like) operators. + +``` +friends.#(last=="Murphy").first >> "Dale" +friends.#(last=="Murphy")#.first >> ["Dale","Jane"] +friends.#(age>45)#.last >> ["Craig","Murphy"] +friends.#(first%"D*").last >> "Murphy" +friends.#(first!%"D*").last >> "Craig" +friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] +``` + +*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was +changed in v1.3.0 as to avoid confusion with the new +[multipath](SYNTAX.md#multipaths) syntax. For backwards compatibility, +`#[...]` will continue to work until the next major release.* + +## Result Type + +GJSON supports the json types `string`, `number`, `bool`, and `null`. +Arrays and Objects are returned as their raw json types. + +The `Result` type holds one of these: + +``` +bool, for JSON booleans +float64, for JSON numbers +string, for JSON string literals +nil, for JSON null +``` + +To directly access the value: + +```go +result.Type // can be String, Number, True, False, Null, or JSON +result.Str // holds the string +result.Num // holds the float64 number +result.Raw // holds the raw json +result.Index // index of raw value in original json, zero means index unknown +result.Indexes // indexes of all the elements that match on a path containing the '#' query character. +``` + +There are a variety of handy functions that work on a result: + +```go +result.Exists() bool +result.Value() interface{} +result.Int() int64 +result.Uint() uint64 +result.Float() float64 +result.String() string +result.Bool() bool +result.Time() time.Time +result.Array() []gjson.Result +result.Map() map[string]gjson.Result +result.Get(path string) Result +result.ForEach(iterator func(key, value Result) bool) +result.Less(token Result, caseSensitive bool) bool +``` + +The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types: + +```go +boolean >> bool +number >> float64 +string >> string +null >> nil +array >> []interface{} +object >> map[string]interface{} +``` + +The `result.Array()` function returns back an array of values. +If the result represents a non-existent value, then an empty array will be returned. +If the result is not a JSON array, the return value will be an array containing one result. + +### 64-bit integers + +The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers. + +```go +result.Int() int64 // -9223372036854775808 to 9223372036854775807 +result.Uint() uint64 // 0 to 18446744073709551615 +``` + +## Modifiers and path chaining + +New in version 1.2 is support for modifier functions and path chaining. + +A modifier is a path component that performs custom processing on the +json. + +Multiple paths can be "chained" together using the pipe character. +This is useful for getting results from a modified query. + +For example, using the built-in `@reverse` modifier on the above json document, +we'll get `children` array and reverse the order: + +``` +"children|@reverse" >> ["Jack","Alex","Sara"] +"children|@reverse|0" >> "Jack" +``` + +There are currently the following built-in modifiers: + +- `@reverse`: Reverse an array or the members of an object. +- `@ugly`: Remove all whitespace from a json document. +- `@pretty`: Make the json document more human readable. +- `@this`: Returns the current element. It can be used to retrieve the root element. +- `@valid`: Ensure the json document is valid. +- `@flatten`: Flattens an array. +- `@join`: Joins multiple objects into a single object. +- `@keys`: Returns an array of keys for an object. +- `@values`: Returns an array of values for an object. +- `@tostr`: Converts json to a string. Wraps a json string. +- `@fromstr`: Converts a string from json. Unwraps a json string. +- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db). +- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf). + +### Modifier arguments + +A modifier may accept an optional argument. The argument can be a valid JSON +document or just characters. + +For example, the `@pretty` modifier takes a json object as its argument. + +``` +@pretty:{"sortKeys":true} +``` + +Which makes the json pretty and orders all of its keys. + +```json +{ + "age":37, + "children": ["Sara","Alex","Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + {"age": 44, "first": "Dale", "last": "Murphy"}, + {"age": 68, "first": "Roger", "last": "Craig"}, + {"age": 47, "first": "Jane", "last": "Murphy"} + ], + "name": {"first": "Tom", "last": "Anderson"} +} +``` + +*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`. +Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.* + +### Custom modifiers + +You can also add custom modifiers. + +For example, here we create a modifier that makes the entire json document upper +or lower case. + +```go +gjson.AddModifier("case", func(json, arg string) string { + if arg == "upper" { + return strings.ToUpper(json) + } + if arg == "lower" { + return strings.ToLower(json) + } + return json +}) +``` + +``` +"children|@case:upper" >> ["SARA","ALEX","JACK"] +"children|@case:lower|@reverse" >> ["jack","alex","sara"] +``` + +## JSON Lines + +There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array. + +For example: + +``` +{"name": "Gilbert", "age": 61} +{"name": "Alexa", "age": 34} +{"name": "May", "age": 57} +{"name": "Deloise", "age": 44} +``` + +``` +..# >> 4 +..1 >> {"name": "Alexa", "age": 34} +..3 >> {"name": "Deloise", "age": 44} +..#.name >> ["Gilbert","Alexa","May","Deloise"] +..#(name="May").age >> 57 +``` + +The `ForEachLines` function will iterate through JSON lines. + +```go +gjson.ForEachLine(json, func(line gjson.Result) bool{ + println(line.String()) + return true +}) +``` + +## Get nested array values + +Suppose you want all the last names from the following json: + +```json +{ + "programmers": [ + { + "firstName": "Janet", + "lastName": "McLaughlin", + }, { + "firstName": "Elliotte", + "lastName": "Hunter", + }, { + "firstName": "Jason", + "lastName": "Harold", + } + ] +} +``` + +You would use the path "programmers.#.lastName" like such: + +```go +result := gjson.Get(json, "programmers.#.lastName") +for _, name := range result.Array() { + println(name.String()) +} +``` + +You can also query an object inside an array: + +```go +name := gjson.Get(json, `programmers.#(lastName="Hunter").firstName`) +println(name.String()) // prints "Elliotte" +``` + +## Iterate through an object or array + +The `ForEach` function allows for quickly iterating through an object or array. +The key and value are passed to the iterator function for objects. +Only the value is passed for arrays. +Returning `false` from an iterator will stop iteration. + +```go +result := gjson.Get(json, "programmers") +result.ForEach(func(key, value gjson.Result) bool { + println(value.String()) + return true // keep iterating +}) +``` + +## Simple Parse and Get + +There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result. + +For example, all of these will return the same result: + +```go +gjson.Parse(json).Get("name").Get("last") +gjson.Get(json, "name").Get("last") +gjson.Get(json, "name.last") +``` + +## Check for the existence of a value + +Sometimes you just want to know if a value exists. + +```go +value := gjson.Get(json, "name.last") +if !value.Exists() { + println("no last name") +} else { + println(value.String()) +} + +// Or as one step +if gjson.Get(json, "name.last").Exists() { + println("has a last name") +} +``` + +## Validate JSON + +The `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results. + +If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON. + +```go +if !gjson.Valid(json) { + return errors.New("invalid json") +} +value := gjson.Get(json, "name.last") +``` + +## Unmarshal to a map + +To unmarshal to a `map[string]interface{}`: + +```go +m, ok := gjson.Parse(json).Value().(map[string]interface{}) +if !ok { + // not a map +} +``` + +## Working with Bytes + +If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`. + +```go +var json []byte = ... +result := gjson.GetBytes(json, path) +``` + +If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern: + +```go +var json []byte = ... +result := gjson.GetBytes(json, path) +var raw []byte +if result.Index > 0 { + raw = json[result.Index:result.Index+len(result.Raw)] +} else { + raw = []byte(result.Raw) +} +``` + +This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`. + +## Performance + +Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), +[ffjson](https://github.com/pquerna/ffjson), +[EasyJSON](https://github.com/mailru/easyjson), +[jsonparser](https://github.com/buger/jsonparser), +and [json-iterator](https://github.com/json-iterator/go) + +``` +BenchmarkGJSONGet-10 17893731 202.1 ns/op 0 B/op 0 allocs/op +BenchmarkGJSONUnmarshalMap-10 1663548 2157 ns/op 1920 B/op 26 allocs/op +BenchmarkJSONUnmarshalMap-10 832236 4279 ns/op 2920 B/op 68 allocs/op +BenchmarkJSONUnmarshalStruct-10 1076475 3219 ns/op 920 B/op 12 allocs/op +BenchmarkJSONDecoder-10 585729 6126 ns/op 3845 B/op 160 allocs/op +BenchmarkFFJSONLexer-10 2508573 1391 ns/op 880 B/op 8 allocs/op +BenchmarkEasyJSONLexer-10 3000000 537.9 ns/op 501 B/op 5 allocs/op +BenchmarkJSONParserGet-10 13707510 263.9 ns/op 21 B/op 0 allocs/op +BenchmarkJSONIterator-10 3000000 561.2 ns/op 693 B/op 14 allocs/op +``` + +JSON document used: + +```json +{ + "widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + } +} +``` + +Each operation was rotated through one of the following search paths: + +``` +widget.window.name +widget.image.hOffset +widget.text.onMouseUp +``` + +** + +*These benchmarks were run on a MacBook Pro M1 Max using Go 1.22 and can be found [here](https://github.com/tidwall/gjson-benchmarks).* diff --git a/vendor/github.com/tidwall/gjson/SYNTAX.md b/vendor/github.com/tidwall/gjson/SYNTAX.md new file mode 100644 index 00000000..a3f0fac2 --- /dev/null +++ b/vendor/github.com/tidwall/gjson/SYNTAX.md @@ -0,0 +1,360 @@ +# GJSON Path Syntax + +A GJSON Path is a text string syntax that describes a search pattern for quickly retrieving values from a JSON payload. + +This document is designed to explain the structure of a GJSON Path through examples. + +- [Path structure](#path-structure) +- [Basic](#basic) +- [Wildcards](#wildcards) +- [Escape Character](#escape-character) +- [Arrays](#arrays) +- [Queries](#queries) +- [Dot vs Pipe](#dot-vs-pipe) +- [Modifiers](#modifiers) +- [Multipaths](#multipaths) +- [Literals](#literals) + +The definitive implementation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson). +Use the [GJSON Playground](https://gjson.dev) to experiment with the syntax online. + +## Path structure + +A GJSON Path is intended to be easily expressed as a series of components separated by a `.` character. + +Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, `!`, and `?`. + +## Example + +Given this JSON + +```json +{ + "name": {"first": "Tom", "last": "Anderson"}, + "age":37, + "children": ["Sara","Alex","Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, + {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, + {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} + ] +} +``` + +The following GJSON Paths evaluate to the accompanying values. + +### Basic + +In many cases you'll just want to retrieve values by object name or array index. + +```go +name.last "Anderson" +name.first "Tom" +age 37 +children ["Sara","Alex","Jack"] +children.0 "Sara" +children.1 "Alex" +friends.1 {"first": "Roger", "last": "Craig", "age": 68} +friends.1.first "Roger" +``` + +### Wildcards + +A key may contain the special wildcard characters `*` and `?`. +The `*` will match on any zero+ characters, and `?` matches on any one character. + +```go +child*.2 "Jack" +c?ildren.0 "Sara" +``` + +### Escape character + +Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`. + +```go +fav\.movie "Deer Hunter" +``` + +You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in your source code. + +```go +// Go +val := gjson.Get(json, "fav\\.movie") // must escape the slash +val := gjson.Get(json, `fav\.movie`) // no need to escape the slash +``` + +```rust +// Rust +let val = gjson::get(json, "fav\\.movie") // must escape the slash +let val = gjson::get(json, r#"fav\.movie"#) // no need to escape the slash +``` + + +### Arrays + +The `#` character allows for digging into JSON Arrays. + +To get the length of an array you'll just use the `#` all by itself. + +```go +friends.# 3 +friends.#.age [44,68,47] +``` + +### Queries + +You can also query an array for the first match by using `#(...)`, or find all matches with `#(...)#`. +Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators, +and the simple pattern matching `%` (like) and `!%` (not like) operators. + +```go +friends.#(last=="Murphy").first "Dale" +friends.#(last=="Murphy")#.first ["Dale","Jane"] +friends.#(age>45)#.last ["Craig","Murphy"] +friends.#(first%"D*").last "Murphy" +friends.#(first!%"D*").last "Craig" +``` + +To query for a non-object value in an array, you can forgo the string to the right of the operator. + +```go +children.#(!%"*a*") "Alex" +children.#(%"*a*")# ["Sara","Jack"] +``` + +Nested queries are allowed. + +```go +friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] +``` + +*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was +changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths) +syntax. For backwards compatibility, `#[...]` will continue to work until the +next major release.* + +The `~` (tilde) operator will convert a value to a boolean before comparison. + +Supported tilde comparison type are: + +``` +~true Converts true-ish values to true +~false Converts false-ish and non-existent values to true +~null Converts null and non-existent values to true +~* Converts any existing value to true +``` + +For example, using the following JSON: + +```json +{ + "vals": [ + { "a": 1, "b": "data" }, + { "a": 2, "b": true }, + { "a": 3, "b": false }, + { "a": 4, "b": "0" }, + { "a": 5, "b": 0 }, + { "a": 6, "b": "1" }, + { "a": 7, "b": 1 }, + { "a": 8, "b": "true" }, + { "a": 9, "b": false }, + { "a": 10, "b": null }, + { "a": 11 } + ] +} +``` + +To query for all true-ish or false-ish values: + +``` +vals.#(b==~true)#.a >> [2,6,7,8] +vals.#(b==~false)#.a >> [3,4,5,9,10,11] +``` + +The last value which was non-existent is treated as `false` + +To query for null and explicit value existence: + +``` +vals.#(b==~null)#.a >> [10,11] +vals.#(b==~*)#.a >> [1,2,3,4,5,6,7,8,9,10] +vals.#(b!=~*)#.a >> [11] +``` + +### Dot vs Pipe + +The `.` is standard separator, but it's also possible to use a `|`. +In most cases they both end up returning the same results. +The cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries). + +Here are some examples + +```go +friends.0.first "Dale" +friends|0.first "Dale" +friends.0|first "Dale" +friends|0|first "Dale" +friends|# 3 +friends.# 3 +friends.#(last="Murphy")# [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}] +friends.#(last="Murphy")#.first ["Dale","Jane"] +friends.#(last="Murphy")#|first +friends.#(last="Murphy")#.0 [] +friends.#(last="Murphy")#|0 {"first": "Dale", "last": "Murphy", "age": 44} +friends.#(last="Murphy")#.# [] +friends.#(last="Murphy")#|# 2 +``` + +Let's break down a few of these. + +The path `friends.#(last="Murphy")#` all by itself results in + +```json +[{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}] +``` + +The `.first` suffix will process the `first` path on each array element *before* returning the results. Which becomes + +```json +["Dale","Jane"] +``` + +But the `|first` suffix actually processes the `first` path *after* the previous result. +Since the previous result is an array, not an object, it's not possible to process +because `first` does not exist. + +Yet, `|0` suffix returns + +```json +{"first": "Dale", "last": "Murphy", "age": 44} +``` + +Because `0` is the first index of the previous result. + +### Modifiers + +A modifier is a path component that performs custom processing on the JSON. + +For example, using the built-in `@reverse` modifier on the above JSON payload will reverse the `children` array: + +```go +children.@reverse ["Jack","Alex","Sara"] +children.@reverse.0 "Jack" +``` + +There are currently the following built-in modifiers: + +- `@reverse`: Reverse an array or the members of an object. +- `@ugly`: Remove all whitespace from JSON. +- `@pretty`: Make the JSON more human readable. +- `@this`: Returns the current element. It can be used to retrieve the root element. +- `@valid`: Ensure the json document is valid. +- `@flatten`: Flattens an array. +- `@join`: Joins multiple objects into a single object. +- `@keys`: Returns an array of keys for an object. +- `@values`: Returns an array of values for an object. +- `@tostr`: Converts json to a string. Wraps a json string. +- `@fromstr`: Converts a string from json. Unwraps a json string. +- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db). +- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf). + +#### Modifier arguments + +A modifier may accept an optional argument. The argument can be a valid JSON payload or just characters. + +For example, the `@pretty` modifier takes a json object as its argument. + +``` +@pretty:{"sortKeys":true} +``` + +Which makes the json pretty and orders all of its keys. + +```json +{ + "age":37, + "children": ["Sara","Alex","Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + {"age": 44, "first": "Dale", "last": "Murphy"}, + {"age": 68, "first": "Roger", "last": "Craig"}, + {"age": 47, "first": "Jane", "last": "Murphy"} + ], + "name": {"first": "Tom", "last": "Anderson"} +} +``` + +*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`. +Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.* + +#### Custom modifiers + +You can also add custom modifiers. + +For example, here we create a modifier which makes the entire JSON payload upper or lower case. + +```go +gjson.AddModifier("case", func(json, arg string) string { + if arg == "upper" { + return strings.ToUpper(json) + } + if arg == "lower" { + return strings.ToLower(json) + } + return json +}) +"children.@case:upper" ["SARA","ALEX","JACK"] +"children.@case:lower.@reverse" ["jack","alex","sara"] +``` + +*Note: Custom modifiers are not yet available in the Rust version* + +### Multipaths + +Starting with v1.3.0, GJSON added the ability to join multiple paths together +to form new documents. Wrapping comma-separated paths between `[...]` or +`{...}` will result in a new array or object, respectively. + +For example, using the given multipath: + +``` +{name.first,age,"the_murphys":friends.#(last="Murphy")#.first} +``` + +Here we selected the first name, age, and the first name for friends with the +last name "Murphy". + +You'll notice that an optional key can be provided, in this case +"the_murphys", to force assign a key to a value. Otherwise, the name of the +actual field will be used, in this case "first". If a name cannot be +determined, then "_" is used. + +This results in + +```json +{"first":"Tom","age":37,"the_murphys":["Dale","Jane"]} +``` + +### Literals + +Starting with v1.12.0, GJSON added support of json literals, which provides a way for constructing static blocks of json. This is can be particularly useful when constructing a new json document using [multipaths](#multipaths). + +A json literal begins with the '!' declaration character. + +For example, using the given multipath: + +``` +{name.first,age,"company":!"Happysoft","employed":!true} +``` + +Here we selected the first name and age. Then add two new fields, "company" and "employed". + +This results in + +```json +{"first":"Tom","age":37,"company":"Happysoft","employed":true} +``` + +*See issue [#249](https://github.com/tidwall/gjson/issues/249) for additional context on JSON Literals.* diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go new file mode 100644 index 00000000..5aa2a4ff --- /dev/null +++ b/vendor/github.com/tidwall/gjson/gjson.go @@ -0,0 +1,3603 @@ +// Package gjson provides searching for json strings. +package gjson + +import ( + "strconv" + "strings" + "time" + "unicode/utf16" + "unicode/utf8" + "unsafe" + + "github.com/tidwall/match" + "github.com/tidwall/pretty" +) + +// Type is Result type +type Type int + +const ( + // Null is a null json value + Null Type = iota + // False is a json false boolean + False + // Number is json number + Number + // String is a json string + String + // True is a json true boolean + True + // JSON is a raw block of JSON + JSON +) + +// String returns a string representation of the type. +func (t Type) String() string { + switch t { + default: + return "" + case Null: + return "Null" + case False: + return "False" + case Number: + return "Number" + case String: + return "String" + case True: + return "True" + case JSON: + return "JSON" + } +} + +// Result represents a json value that is returned from Get(). +type Result struct { + // Type is the json type + Type Type + // Raw is the raw json + Raw string + // Str is the json string + Str string + // Num is the json number + Num float64 + // Index of raw value in original json, zero means index unknown + Index int + // Indexes of all the elements that match on a path containing the '#' + // query character. + Indexes []int +} + +// String returns a string representation of the value. +func (t Result) String() string { + switch t.Type { + default: + return "" + case False: + return "false" + case Number: + if len(t.Raw) == 0 { + // calculated result + return strconv.FormatFloat(t.Num, 'f', -1, 64) + } + var i int + if t.Raw[0] == '-' { + i++ + } + for ; i < len(t.Raw); i++ { + if t.Raw[i] < '0' || t.Raw[i] > '9' { + return strconv.FormatFloat(t.Num, 'f', -1, 64) + } + } + return t.Raw + case String: + return t.Str + case JSON: + return t.Raw + case True: + return "true" + } +} + +// Bool returns an boolean representation. +func (t Result) Bool() bool { + switch t.Type { + default: + return false + case True: + return true + case String: + b, _ := strconv.ParseBool(strings.ToLower(t.Str)) + return b + case Number: + return t.Num != 0 + } +} + +// Int returns an integer representation. +func (t Result) Int() int64 { + switch t.Type { + default: + return 0 + case True: + return 1 + case String: + n, _ := parseInt(t.Str) + return n + case Number: + // try to directly convert the float64 to int64 + i, ok := safeInt(t.Num) + if ok { + return i + } + // now try to parse the raw string + i, ok = parseInt(t.Raw) + if ok { + return i + } + // fallback to a standard conversion + return int64(t.Num) + } +} + +// Uint returns an unsigned integer representation. +func (t Result) Uint() uint64 { + switch t.Type { + default: + return 0 + case True: + return 1 + case String: + n, _ := parseUint(t.Str) + return n + case Number: + // try to directly convert the float64 to uint64 + i, ok := safeInt(t.Num) + if ok && i >= 0 { + return uint64(i) + } + // now try to parse the raw string + u, ok := parseUint(t.Raw) + if ok { + return u + } + // fallback to a standard conversion + return uint64(t.Num) + } +} + +// Float returns an float64 representation. +func (t Result) Float() float64 { + switch t.Type { + default: + return 0 + case True: + return 1 + case String: + n, _ := strconv.ParseFloat(t.Str, 64) + return n + case Number: + return t.Num + } +} + +// Time returns a time.Time representation. +func (t Result) Time() time.Time { + res, _ := time.Parse(time.RFC3339, t.String()) + return res +} + +// Array returns back an array of values. +// If the result represents a null value or is non-existent, then an empty +// array will be returned. +// If the result is not a JSON array, the return value will be an +// array containing one result. +func (t Result) Array() []Result { + if t.Type == Null { + return []Result{} + } + if !t.IsArray() { + return []Result{t} + } + r := t.arrayOrMap('[', false) + return r.a +} + +// IsObject returns true if the result value is a JSON object. +func (t Result) IsObject() bool { + return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{' +} + +// IsArray returns true if the result value is a JSON array. +func (t Result) IsArray() bool { + return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '[' +} + +// IsBool returns true if the result value is a JSON boolean. +func (t Result) IsBool() bool { + return t.Type == True || t.Type == False +} + +// ForEach iterates through values. +// If the result represents a non-existent value, then no values will be +// iterated. If the result is an Object, the iterator will pass the key and +// value of each item. If the result is an Array, the iterator will only pass +// the value of each item. If the result is not a JSON array or object, the +// iterator will pass back one value equal to the result. +func (t Result) ForEach(iterator func(key, value Result) bool) { + if !t.Exists() { + return + } + if t.Type != JSON { + iterator(Result{}, t) + return + } + json := t.Raw + var obj bool + var i int + var key, value Result + for ; i < len(json); i++ { + if json[i] == '{' { + i++ + key.Type = String + obj = true + break + } else if json[i] == '[' { + i++ + key.Type = Number + key.Num = -1 + break + } + if json[i] > ' ' { + return + } + } + var str string + var vesc bool + var ok bool + var idx int + for ; i < len(json); i++ { + if obj { + if json[i] != '"' { + continue + } + s := i + i, str, vesc, ok = parseString(json, i+1) + if !ok { + return + } + if vesc { + key.Str = unescape(str[1 : len(str)-1]) + } else { + key.Str = str[1 : len(str)-1] + } + key.Raw = str + key.Index = s + t.Index + } else { + key.Num += 1 + } + for ; i < len(json); i++ { + if json[i] <= ' ' || json[i] == ',' || json[i] == ':' { + continue + } + break + } + s := i + i, value, ok = parseAny(json, i, true) + if !ok { + return + } + if t.Indexes != nil { + if idx < len(t.Indexes) { + value.Index = t.Indexes[idx] + } + } else { + value.Index = s + t.Index + } + if !iterator(key, value) { + return + } + idx++ + } +} + +// Map returns back a map of values. The result should be a JSON object. +// If the result is not a JSON object, the return value will be an empty map. +func (t Result) Map() map[string]Result { + if t.Type != JSON { + return map[string]Result{} + } + r := t.arrayOrMap('{', false) + return r.o +} + +// Get searches result for the specified path. +// The result should be a JSON array or object. +func (t Result) Get(path string) Result { + r := Get(t.Raw, path) + if r.Indexes != nil { + for i := 0; i < len(r.Indexes); i++ { + r.Indexes[i] += t.Index + } + } else { + r.Index += t.Index + } + return r +} + +type arrayOrMapResult struct { + a []Result + ai []interface{} + o map[string]Result + oi map[string]interface{} + vc byte +} + +func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) { + var json = t.Raw + var i int + var value Result + var count int + var key Result + if vc == 0 { + for ; i < len(json); i++ { + if json[i] == '{' || json[i] == '[' { + r.vc = json[i] + i++ + break + } + if json[i] > ' ' { + goto end + } + } + } else { + for ; i < len(json); i++ { + if json[i] == vc { + i++ + break + } + if json[i] > ' ' { + goto end + } + } + r.vc = vc + } + if r.vc == '{' { + if valueize { + r.oi = make(map[string]interface{}) + } else { + r.o = make(map[string]Result) + } + } else { + if valueize { + r.ai = make([]interface{}, 0) + } else { + r.a = make([]Result, 0) + } + } + for ; i < len(json); i++ { + if json[i] <= ' ' { + continue + } + // get next value + if json[i] == ']' || json[i] == '}' { + break + } + switch json[i] { + default: + if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { + value.Type = Number + value.Raw, value.Num = tonum(json[i:]) + value.Str = "" + } else { + continue + } + case '{', '[': + value.Type = JSON + value.Raw = squash(json[i:]) + value.Str, value.Num = "", 0 + case 'n': + value.Type = Null + value.Raw = tolit(json[i:]) + value.Str, value.Num = "", 0 + case 't': + value.Type = True + value.Raw = tolit(json[i:]) + value.Str, value.Num = "", 0 + case 'f': + value.Type = False + value.Raw = tolit(json[i:]) + value.Str, value.Num = "", 0 + case '"': + value.Type = String + value.Raw, value.Str = tostr(json[i:]) + value.Num = 0 + } + value.Index = i + t.Index + + i += len(value.Raw) - 1 + + if r.vc == '{' { + if count%2 == 0 { + key = value + } else { + if valueize { + if _, ok := r.oi[key.Str]; !ok { + r.oi[key.Str] = value.Value() + } + } else { + if _, ok := r.o[key.Str]; !ok { + r.o[key.Str] = value + } + } + } + count++ + } else { + if valueize { + r.ai = append(r.ai, value.Value()) + } else { + r.a = append(r.a, value) + } + } + } +end: + if t.Indexes != nil { + if len(t.Indexes) != len(r.a) { + for i := 0; i < len(r.a); i++ { + r.a[i].Index = 0 + } + } else { + for i := 0; i < len(r.a); i++ { + r.a[i].Index = t.Indexes[i] + } + } + } + return +} + +// Parse parses the json and returns a result. +// +// This function expects that the json is well-formed, and does not validate. +// Invalid json will not panic, but it may return back unexpected results. +// If you are consuming JSON from an unpredictable source then you may want to +// use the Valid function first. +func Parse(json string) Result { + var value Result + i := 0 + for ; i < len(json); i++ { + if json[i] == '{' || json[i] == '[' { + value.Type = JSON + value.Raw = json[i:] // just take the entire raw + break + } + if json[i] <= ' ' { + continue + } + switch json[i] { + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + value.Type = Number + value.Raw, value.Num = tonum(json[i:]) + case 'n': + if i+1 < len(json) && json[i+1] != 'u' { + // nan + value.Type = Number + value.Raw, value.Num = tonum(json[i:]) + } else { + // null + value.Type = Null + value.Raw = tolit(json[i:]) + } + case 't': + value.Type = True + value.Raw = tolit(json[i:]) + case 'f': + value.Type = False + value.Raw = tolit(json[i:]) + case '"': + value.Type = String + value.Raw, value.Str = tostr(json[i:]) + default: + return Result{} + } + break + } + if value.Exists() { + value.Index = i + } + return value +} + +// ParseBytes parses the json and returns a result. +// If working with bytes, this method preferred over Parse(string(data)) +func ParseBytes(json []byte) Result { + return Parse(string(json)) +} + +func squash(json string) string { + // expects that the lead character is a '[' or '{' or '(' or '"' + // squash the value, ignoring all nested arrays and objects. + var i, depth int + if json[0] != '"' { + i, depth = 1, 1 + } + for ; i < len(json); i++ { + if json[i] >= '"' && json[i] <= '}' { + switch json[i] { + case '"': + i++ + s2 := i + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > s2-1; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + break + } + } + if depth == 0 { + if i >= len(json) { + return json + } + return json[:i+1] + } + case '{', '[', '(': + depth++ + case '}', ']', ')': + depth-- + if depth == 0 { + return json[:i+1] + } + } + } + } + return json +} + +func tonum(json string) (raw string, num float64) { + for i := 1; i < len(json); i++ { + // less than dash might have valid characters + if json[i] <= '-' { + if json[i] <= ' ' || json[i] == ',' { + // break on whitespace and comma + raw = json[:i] + num, _ = strconv.ParseFloat(raw, 64) + return + } + // could be a '+' or '-'. let's assume so. + } else if json[i] == ']' || json[i] == '}' { + // break on ']' or '}' + raw = json[:i] + num, _ = strconv.ParseFloat(raw, 64) + return + } + } + raw = json + num, _ = strconv.ParseFloat(raw, 64) + return +} + +func tolit(json string) (raw string) { + for i := 1; i < len(json); i++ { + if json[i] < 'a' || json[i] > 'z' { + return json[:i] + } + } + return json +} + +func tostr(json string) (raw string, str string) { + // expects that the lead character is a '"' + for i := 1; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + return json[:i+1], json[1:i] + } + if json[i] == '\\' { + i++ + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > 0; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + return json[:i+1], unescape(json[1:i]) + } + } + var ret string + if i+1 < len(json) { + ret = json[:i+1] + } else { + ret = json[:i] + } + return ret, unescape(json[1:i]) + } + } + return json, json[1:] +} + +// Exists returns true if value exists. +// +// if gjson.Get(json, "name.last").Exists(){ +// println("value exists") +// } +func (t Result) Exists() bool { + return t.Type != Null || len(t.Raw) != 0 +} + +// Value returns one of these types: +// +// bool, for JSON booleans +// float64, for JSON numbers +// Number, for JSON numbers +// string, for JSON string literals +// nil, for JSON null +// map[string]interface{}, for JSON objects +// []interface{}, for JSON arrays +func (t Result) Value() interface{} { + if t.Type == String { + return t.Str + } + switch t.Type { + default: + return nil + case False: + return false + case Number: + return t.Num + case JSON: + r := t.arrayOrMap(0, true) + if r.vc == '{' { + return r.oi + } else if r.vc == '[' { + return r.ai + } + return nil + case True: + return true + } +} + +func parseString(json string, i int) (int, string, bool, bool) { + var s = i + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + return i + 1, json[s-1 : i+1], false, true + } + if json[i] == '\\' { + i++ + for ; i < len(json); i++ { + if json[i] > '\\' { + continue + } + if json[i] == '"' { + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > 0; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + return i + 1, json[s-1 : i+1], true, true + } + } + break + } + } + return i, json[s-1:], false, false +} + +func parseNumber(json string, i int) (int, string) { + var s = i + i++ + for ; i < len(json); i++ { + if json[i] <= ' ' || json[i] == ',' || json[i] == ']' || + json[i] == '}' { + return i, json[s:i] + } + } + return i, json[s:] +} + +func parseLiteral(json string, i int) (int, string) { + var s = i + i++ + for ; i < len(json); i++ { + if json[i] < 'a' || json[i] > 'z' { + return i, json[s:i] + } + } + return i, json[s:] +} + +type arrayPathResult struct { + part string + path string + pipe string + piped bool + more bool + alogok bool + arrch bool + alogkey string + query struct { + on bool + all bool + path string + op string + value string + } +} + +func parseArrayPath(path string) (r arrayPathResult) { + for i := 0; i < len(path); i++ { + if path[i] == '|' { + r.part = path[:i] + r.pipe = path[i+1:] + r.piped = true + return + } + if path[i] == '.' { + r.part = path[:i] + if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1:]) { + r.pipe = path[i+1:] + r.piped = true + } else { + r.path = path[i+1:] + r.more = true + } + return + } + if path[i] == '#' { + r.arrch = true + if i == 0 && len(path) > 1 { + if path[1] == '.' { + r.alogok = true + r.alogkey = path[2:] + r.path = path[:1] + } else if path[1] == '[' || path[1] == '(' { + // query + r.query.on = true + qpath, op, value, _, fi, vesc, ok := + parseQuery(path[i:]) + if !ok { + // bad query, end now + break + } + if len(value) >= 2 && value[0] == '"' && + value[len(value)-1] == '"' { + value = value[1 : len(value)-1] + if vesc { + value = unescape(value) + } + } + r.query.path = qpath + r.query.op = op + r.query.value = value + + i = fi - 1 + if i+1 < len(path) && path[i+1] == '#' { + r.query.all = true + } + } + } + continue + } + } + r.part = path + r.path = "" + return +} + +// splitQuery takes a query and splits it into three parts: +// +// path, op, middle, and right. +// +// So for this query: +// +// #(first_name=="Murphy").last +// +// Becomes +// +// first_name # path +// =="Murphy" # middle +// .last # right +// +// Or, +// +// #(service_roles.#(=="one")).cap +// +// Becomes +// +// service_roles.#(=="one") # path +// # middle +// .cap # right +func parseQuery(query string) ( + path, op, value, remain string, i int, vesc, ok bool, +) { + if len(query) < 2 || query[0] != '#' || + (query[1] != '(' && query[1] != '[') { + return "", "", "", "", i, false, false + } + i = 2 + j := 0 // start of value part + depth := 1 + for ; i < len(query); i++ { + if depth == 1 && j == 0 { + switch query[i] { + case '!', '=', '<', '>', '%': + // start of the value part + j = i + continue + } + } + if query[i] == '\\' { + i++ + } else if query[i] == '[' || query[i] == '(' { + depth++ + } else if query[i] == ']' || query[i] == ')' { + depth-- + if depth == 0 { + break + } + } else if query[i] == '"' { + // inside selector string, balance quotes + i++ + for ; i < len(query); i++ { + if query[i] == '\\' { + vesc = true + i++ + } else if query[i] == '"' { + break + } + } + } + } + if depth > 0 { + return "", "", "", "", i, false, false + } + if j > 0 { + path = trim(query[2:j]) + value = trim(query[j:i]) + remain = query[i+1:] + // parse the compare op from the value + var opsz int + switch { + case len(value) == 1: + opsz = 1 + case value[0] == '!' && value[1] == '=': + opsz = 2 + case value[0] == '!' && value[1] == '%': + opsz = 2 + case value[0] == '<' && value[1] == '=': + opsz = 2 + case value[0] == '>' && value[1] == '=': + opsz = 2 + case value[0] == '=' && value[1] == '=': + value = value[1:] + opsz = 1 + case value[0] == '<': + opsz = 1 + case value[0] == '>': + opsz = 1 + case value[0] == '=': + opsz = 1 + case value[0] == '%': + opsz = 1 + } + op = value[:opsz] + value = trim(value[opsz:]) + } else { + path = trim(query[2:i]) + remain = query[i+1:] + } + return path, op, value, remain, i + 1, vesc, true +} + +func trim(s string) string { +left: + if len(s) > 0 && s[0] <= ' ' { + s = s[1:] + goto left + } +right: + if len(s) > 0 && s[len(s)-1] <= ' ' { + s = s[:len(s)-1] + goto right + } + return s +} + +// peek at the next byte and see if it's a '@', '[', or '{'. +func isDotPiperChar(s string) bool { + if DisableModifiers { + return false + } + c := s[0] + if c == '@' { + // check that the next component is *not* a modifier. + i := 1 + for ; i < len(s); i++ { + if s[i] == '.' || s[i] == '|' || s[i] == ':' { + break + } + } + _, ok := modifiers[s[1:i]] + return ok + } + return c == '[' || c == '{' +} + +type objectPathResult struct { + part string + path string + pipe string + piped bool + wild bool + more bool +} + +func parseObjectPath(path string) (r objectPathResult) { + for i := 0; i < len(path); i++ { + if path[i] == '|' { + r.part = path[:i] + r.pipe = path[i+1:] + r.piped = true + return + } + if path[i] == '.' { + r.part = path[:i] + if i < len(path)-1 && isDotPiperChar(path[i+1:]) { + r.pipe = path[i+1:] + r.piped = true + } else { + r.path = path[i+1:] + r.more = true + } + return + } + if path[i] == '*' || path[i] == '?' { + r.wild = true + continue + } + if path[i] == '\\' { + // go into escape mode. this is a slower path that + // strips off the escape character from the part. + epart := []byte(path[:i]) + i++ + if i < len(path) { + epart = append(epart, path[i]) + i++ + for ; i < len(path); i++ { + if path[i] == '\\' { + i++ + if i < len(path) { + epart = append(epart, path[i]) + } + continue + } else if path[i] == '.' { + r.part = string(epart) + if i < len(path)-1 && isDotPiperChar(path[i+1:]) { + r.pipe = path[i+1:] + r.piped = true + } else { + r.path = path[i+1:] + r.more = true + } + return + } else if path[i] == '|' { + r.part = string(epart) + r.pipe = path[i+1:] + r.piped = true + return + } else if path[i] == '*' || path[i] == '?' { + r.wild = true + } + epart = append(epart, path[i]) + } + } + // append the last part + r.part = string(epart) + return + } + } + r.part = path + return +} + +var vchars = [256]byte{ + '"': 2, '{': 3, '(': 3, '[': 3, '}': 1, ')': 1, ']': 1, +} + +func parseSquash(json string, i int) (int, string) { + // expects that the lead character is a '[' or '{' or '(' + // squash the value, ignoring all nested arrays and objects. + // the first '[' or '{' or '(' has already been read + s := i + i++ + depth := 1 + var c byte + for i < len(json) { + for i < len(json)-8 { + jslice := json[i : i+8] + c = vchars[jslice[0]] + if c != 0 { + i += 0 + goto token + } + c = vchars[jslice[1]] + if c != 0 { + i += 1 + goto token + } + c = vchars[jslice[2]] + if c != 0 { + i += 2 + goto token + } + c = vchars[jslice[3]] + if c != 0 { + i += 3 + goto token + } + c = vchars[jslice[4]] + if c != 0 { + i += 4 + goto token + } + c = vchars[jslice[5]] + if c != 0 { + i += 5 + goto token + } + c = vchars[jslice[6]] + if c != 0 { + i += 6 + goto token + } + c = vchars[jslice[7]] + if c != 0 { + i += 7 + goto token + } + i += 8 + } + c = vchars[json[i]] + if c == 0 { + i++ + continue + } + token: + if c == 2 { + // '"' string + i++ + s2 := i + nextquote: + for i < len(json)-8 { + jslice := json[i : i+8] + if jslice[0] == '"' { + i += 0 + goto strchkesc + } + if jslice[1] == '"' { + i += 1 + goto strchkesc + } + if jslice[2] == '"' { + i += 2 + goto strchkesc + } + if jslice[3] == '"' { + i += 3 + goto strchkesc + } + if jslice[4] == '"' { + i += 4 + goto strchkesc + } + if jslice[5] == '"' { + i += 5 + goto strchkesc + } + if jslice[6] == '"' { + i += 6 + goto strchkesc + } + if jslice[7] == '"' { + i += 7 + goto strchkesc + } + i += 8 + } + goto strchkstd + strchkesc: + if json[i-1] != '\\' { + i++ + continue + } + strchkstd: + for i < len(json) { + if json[i] > '\\' || json[i] != '"' { + i++ + continue + } + // look for an escaped slash + if json[i-1] == '\\' { + n := 0 + for j := i - 2; j > s2-1; j-- { + if json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + i++ + goto nextquote + } + } + break + } + } else { + // '{', '[', '(', '}', ']', ')' + // open close tokens + depth += int(c) - 2 + if depth == 0 { + i++ + return i, json[s:i] + } + } + i++ + } + return i, json[s:] +} + +func parseObject(c *parseContext, i int, path string) (int, bool) { + var pmatch, kesc, vesc, ok, hit bool + var key, val string + rp := parseObjectPath(path) + if !rp.more && rp.piped { + c.pipe = rp.pipe + c.piped = true + } + for i < len(c.json) { + for ; i < len(c.json); i++ { + if c.json[i] == '"' { + // parse_key_string + // this is slightly different from getting s string value + // because we don't need the outer quotes. + i++ + var s = i + for ; i < len(c.json); i++ { + if c.json[i] > '\\' { + continue + } + if c.json[i] == '"' { + i, key, kesc, ok = i+1, c.json[s:i], false, true + goto parse_key_string_done + } + if c.json[i] == '\\' { + i++ + for ; i < len(c.json); i++ { + if c.json[i] > '\\' { + continue + } + if c.json[i] == '"' { + // look for an escaped slash + if c.json[i-1] == '\\' { + n := 0 + for j := i - 2; j > 0; j-- { + if c.json[j] != '\\' { + break + } + n++ + } + if n%2 == 0 { + continue + } + } + i, key, kesc, ok = i+1, c.json[s:i], true, true + goto parse_key_string_done + } + } + break + } + } + key, kesc, ok = c.json[s:], false, false + parse_key_string_done: + break + } + if c.json[i] == '}' { + return i + 1, false + } + } + if !ok { + return i, false + } + if rp.wild { + if kesc { + pmatch = matchLimit(unescape(key), rp.part) + } else { + pmatch = matchLimit(key, rp.part) + } + } else { + if kesc { + pmatch = rp.part == unescape(key) + } else { + pmatch = rp.part == key + } + } + hit = pmatch && !rp.more + for ; i < len(c.json); i++ { + var num bool + switch c.json[i] { + default: + continue + case '"': + i++ + i, val, vesc, ok = parseString(c.json, i) + if !ok { + return i, false + } + if hit { + if vesc { + c.value.Str = unescape(val[1 : len(val)-1]) + } else { + c.value.Str = val[1 : len(val)-1] + } + c.value.Raw = val + c.value.Type = String + return i, true + } + case '{': + if pmatch && !hit { + i, hit = parseObject(c, i+1, rp.path) + if hit { + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case '[': + if pmatch && !hit { + i, hit = parseArray(c, i+1, rp.path) + if hit { + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case 'n': + if i+1 < len(c.json) && c.json[i+1] != 'u' { + num = true + break + } + fallthrough + case 't', 'f': + vc := c.json[i] + i, val = parseLiteral(c.json, i) + if hit { + c.value.Raw = val + switch vc { + case 't': + c.value.Type = True + case 'f': + c.value.Type = False + } + return i, true + } + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + num = true + } + if num { + i, val = parseNumber(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = Number + c.value.Num, _ = strconv.ParseFloat(val, 64) + return i, true + } + } + break + } + } + return i, false +} + +// matchLimit will limit the complexity of the match operation to avoid ReDos +// attacks from arbitrary inputs. +// See the github.com/tidwall/match.MatchLimit function for more information. +func matchLimit(str, pattern string) bool { + matched, _ := match.MatchLimit(str, pattern, 10000) + return matched +} + +func falseish(t Result) bool { + switch t.Type { + case Null: + return true + case False: + return true + case String: + b, err := strconv.ParseBool(strings.ToLower(t.Str)) + if err != nil { + return false + } + return !b + case Number: + return t.Num == 0 + default: + return false + } +} + +func trueish(t Result) bool { + switch t.Type { + case True: + return true + case String: + b, err := strconv.ParseBool(strings.ToLower(t.Str)) + if err != nil { + return false + } + return b + case Number: + return t.Num != 0 + default: + return false + } +} + +func nullish(t Result) bool { + return t.Type == Null +} + +func queryMatches(rp *arrayPathResult, value Result) bool { + rpv := rp.query.value + if len(rpv) > 0 { + if rpv[0] == '~' { + // convert to bool + rpv = rpv[1:] + var ish, ok bool + switch rpv { + case "*": + ish, ok = value.Exists(), true + case "null": + ish, ok = nullish(value), true + case "true": + ish, ok = trueish(value), true + case "false": + ish, ok = falseish(value), true + } + if ok { + rpv = "true" + if ish { + value = Result{Type: True} + } else { + value = Result{Type: False} + } + } else { + rpv = "" + value = Result{} + } + } + } + if !value.Exists() { + return false + } + if rp.query.op == "" { + // the query is only looking for existence, such as: + // friends.#(name) + // which makes sure that the array "friends" has an element of + // "name" that exists + return true + } + switch value.Type { + case String: + switch rp.query.op { + case "=": + return value.Str == rpv + case "!=": + return value.Str != rpv + case "<": + return value.Str < rpv + case "<=": + return value.Str <= rpv + case ">": + return value.Str > rpv + case ">=": + return value.Str >= rpv + case "%": + return matchLimit(value.Str, rpv) + case "!%": + return !matchLimit(value.Str, rpv) + } + case Number: + rpvn, _ := strconv.ParseFloat(rpv, 64) + switch rp.query.op { + case "=": + return value.Num == rpvn + case "!=": + return value.Num != rpvn + case "<": + return value.Num < rpvn + case "<=": + return value.Num <= rpvn + case ">": + return value.Num > rpvn + case ">=": + return value.Num >= rpvn + } + case True: + switch rp.query.op { + case "=": + return rpv == "true" + case "!=": + return rpv != "true" + case ">": + return rpv == "false" + case ">=": + return true + } + case False: + switch rp.query.op { + case "=": + return rpv == "false" + case "!=": + return rpv != "false" + case "<": + return rpv == "true" + case "<=": + return true + } + } + return false +} +func parseArray(c *parseContext, i int, path string) (int, bool) { + var pmatch, vesc, ok, hit bool + var val string + var h int + var alog []int + var partidx int + var multires []byte + var queryIndexes []int + rp := parseArrayPath(path) + if !rp.arrch { + n, ok := parseUint(rp.part) + if !ok { + partidx = -1 + } else { + partidx = int(n) + } + } + if !rp.more && rp.piped { + c.pipe = rp.pipe + c.piped = true + } + + procQuery := func(qval Result) bool { + if rp.query.all { + if len(multires) == 0 { + multires = append(multires, '[') + } + } + var tmp parseContext + tmp.value = qval + fillIndex(c.json, &tmp) + parentIndex := tmp.value.Index + var res Result + if qval.Type == JSON { + res = qval.Get(rp.query.path) + } else { + if rp.query.path != "" { + return false + } + res = qval + } + if queryMatches(&rp, res) { + if rp.more { + left, right, ok := splitPossiblePipe(rp.path) + if ok { + rp.path = left + c.pipe = right + c.piped = true + } + res = qval.Get(rp.path) + } else { + res = qval + } + if rp.query.all { + raw := res.Raw + if len(raw) == 0 { + raw = res.String() + } + if raw != "" { + if len(multires) > 1 { + multires = append(multires, ',') + } + multires = append(multires, raw...) + queryIndexes = append(queryIndexes, res.Index+parentIndex) + } + } else { + c.value = res + return true + } + } + return false + } + for i < len(c.json)+1 { + if !rp.arrch { + pmatch = partidx == h + hit = pmatch && !rp.more + } + h++ + if rp.alogok { + alog = append(alog, i) + } + for ; ; i++ { + var ch byte + if i > len(c.json) { + break + } else if i == len(c.json) { + ch = ']' + } else { + ch = c.json[i] + } + var num bool + switch ch { + default: + continue + case '"': + i++ + i, val, vesc, ok = parseString(c.json, i) + if !ok { + return i, false + } + if rp.query.on { + var qval Result + if vesc { + qval.Str = unescape(val[1 : len(val)-1]) + } else { + qval.Str = val[1 : len(val)-1] + } + qval.Raw = val + qval.Type = String + if procQuery(qval) { + return i, true + } + } else if hit { + if rp.alogok { + break + } + if vesc { + c.value.Str = unescape(val[1 : len(val)-1]) + } else { + c.value.Str = val[1 : len(val)-1] + } + c.value.Raw = val + c.value.Type = String + return i, true + } + case '{': + if pmatch && !hit { + i, hit = parseObject(c, i+1, rp.path) + if hit { + if rp.alogok { + break + } + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if rp.query.on { + if procQuery(Result{Raw: val, Type: JSON}) { + return i, true + } + } else if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case '[': + if pmatch && !hit { + i, hit = parseArray(c, i+1, rp.path) + if hit { + if rp.alogok { + break + } + return i, true + } + } else { + i, val = parseSquash(c.json, i) + if rp.query.on { + if procQuery(Result{Raw: val, Type: JSON}) { + return i, true + } + } else if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = JSON + return i, true + } + } + case 'n': + if i+1 < len(c.json) && c.json[i+1] != 'u' { + num = true + break + } + fallthrough + case 't', 'f': + vc := c.json[i] + i, val = parseLiteral(c.json, i) + if rp.query.on { + var qval Result + qval.Raw = val + switch vc { + case 't': + qval.Type = True + case 'f': + qval.Type = False + } + if procQuery(qval) { + return i, true + } + } else if hit { + if rp.alogok { + break + } + c.value.Raw = val + switch vc { + case 't': + c.value.Type = True + case 'f': + c.value.Type = False + } + return i, true + } + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + num = true + case ']': + if rp.arrch && rp.part == "#" { + if rp.alogok { + left, right, ok := splitPossiblePipe(rp.alogkey) + if ok { + rp.alogkey = left + c.pipe = right + c.piped = true + } + var indexes = make([]int, 0, 64) + var jsons = make([]byte, 0, 64) + jsons = append(jsons, '[') + for j, k := 0, 0; j < len(alog); j++ { + idx := alog[j] + for idx < len(c.json) { + switch c.json[idx] { + case ' ', '\t', '\r', '\n': + idx++ + continue + } + break + } + if idx < len(c.json) && c.json[idx] != ']' { + _, res, ok := parseAny(c.json, idx, true) + if ok { + res := res.Get(rp.alogkey) + if res.Exists() { + if k > 0 { + jsons = append(jsons, ',') + } + raw := res.Raw + if len(raw) == 0 { + raw = res.String() + } + jsons = append(jsons, []byte(raw)...) + indexes = append(indexes, res.Index) + k++ + } + } + } + } + jsons = append(jsons, ']') + c.value.Type = JSON + c.value.Raw = string(jsons) + c.value.Indexes = indexes + return i + 1, true + } + if rp.alogok { + break + } + + c.value.Type = Number + c.value.Num = float64(h - 1) + c.value.Raw = strconv.Itoa(h - 1) + c.calcd = true + return i + 1, true + } + if !c.value.Exists() { + if len(multires) > 0 { + c.value = Result{ + Raw: string(append(multires, ']')), + Type: JSON, + Indexes: queryIndexes, + } + } else if rp.query.all { + c.value = Result{ + Raw: "[]", + Type: JSON, + } + } + } + return i + 1, false + } + if num { + i, val = parseNumber(c.json, i) + if rp.query.on { + var qval Result + qval.Raw = val + qval.Type = Number + qval.Num, _ = strconv.ParseFloat(val, 64) + if procQuery(qval) { + return i, true + } + } else if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = Number + c.value.Num, _ = strconv.ParseFloat(val, 64) + return i, true + } + } + break + } + } + return i, false +} + +func splitPossiblePipe(path string) (left, right string, ok bool) { + // take a quick peek for the pipe character. If found we'll split the piped + // part of the path into the c.pipe field and shorten the rp. + var possible bool + for i := 0; i < len(path); i++ { + if path[i] == '|' { + possible = true + break + } + } + if !possible { + return + } + + if len(path) > 0 && path[0] == '{' { + squashed := squash(path[1:]) + if len(squashed) < len(path)-1 { + squashed = path[:len(squashed)+1] + remain := path[len(squashed):] + if remain[0] == '|' { + return squashed, remain[1:], true + } + } + return + } + + // split the left and right side of the path with the pipe character as + // the delimiter. This is a little tricky because we'll need to basically + // parse the entire path. + for i := 0; i < len(path); i++ { + if path[i] == '\\' { + i++ + } else if path[i] == '.' { + if i == len(path)-1 { + return + } + if path[i+1] == '#' { + i += 2 + if i == len(path) { + return + } + if path[i] == '[' || path[i] == '(' { + var start, end byte + if path[i] == '[' { + start, end = '[', ']' + } else { + start, end = '(', ')' + } + // inside selector, balance brackets + i++ + depth := 1 + for ; i < len(path); i++ { + if path[i] == '\\' { + i++ + } else if path[i] == start { + depth++ + } else if path[i] == end { + depth-- + if depth == 0 { + break + } + } else if path[i] == '"' { + // inside selector string, balance quotes + i++ + for ; i < len(path); i++ { + if path[i] == '\\' { + i++ + } else if path[i] == '"' { + break + } + } + } + } + } + } + } else if path[i] == '|' { + return path[:i], path[i+1:], true + } + } + return +} + +// ForEachLine iterates through lines of JSON as specified by the JSON Lines +// format (http://jsonlines.org/). +// Each line is returned as a GJSON Result. +func ForEachLine(json string, iterator func(line Result) bool) { + var res Result + var i int + for { + i, res, _ = parseAny(json, i, true) + if !res.Exists() { + break + } + if !iterator(res) { + return + } + } +} + +type subSelector struct { + name string + path string +} + +// parseSubSelectors returns the subselectors belonging to a '[path1,path2]' or +// '{"field1":path1,"field2":path2}' type subSelection. It's expected that the +// first character in path is either '[' or '{', and has already been checked +// prior to calling this function. +func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) { + modifier := 0 + depth := 1 + colon := 0 + start := 1 + i := 1 + pushSel := func() { + var sel subSelector + if colon == 0 { + sel.path = path[start:i] + } else { + sel.name = path[start:colon] + sel.path = path[colon+1 : i] + } + sels = append(sels, sel) + colon = 0 + modifier = 0 + start = i + 1 + } + for ; i < len(path); i++ { + switch path[i] { + case '\\': + i++ + case '@': + if modifier == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') { + modifier = i + } + case ':': + if modifier == 0 && colon == 0 && depth == 1 { + colon = i + } + case ',': + if depth == 1 { + pushSel() + } + case '"': + i++ + loop: + for ; i < len(path); i++ { + switch path[i] { + case '\\': + i++ + case '"': + break loop + } + } + case '[', '(', '{': + depth++ + case ']', ')', '}': + depth-- + if depth == 0 { + pushSel() + path = path[i+1:] + return sels, path, true + } + } + } + return +} + +// nameOfLast returns the name of the last component +func nameOfLast(path string) string { + for i := len(path) - 1; i >= 0; i-- { + if path[i] == '|' || path[i] == '.' { + if i > 0 { + if path[i-1] == '\\' { + continue + } + } + return path[i+1:] + } + } + return path +} + +func isSimpleName(component string) bool { + for i := 0; i < len(component); i++ { + if component[i] < ' ' { + return false + } + switch component[i] { + case '[', ']', '{', '}', '(', ')', '#', '|', '!': + return false + } + } + return true +} + +var hexchars = [...]byte{ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', +} + +func appendHex16(dst []byte, x uint16) []byte { + return append(dst, + hexchars[x>>12&0xF], hexchars[x>>8&0xF], + hexchars[x>>4&0xF], hexchars[x>>0&0xF], + ) +} + +// DisableEscapeHTML will disable the automatic escaping of certain +// "problamatic" HTML characters when encoding to JSON. +// These character include '>', '<' and '&', which get escaped to \u003e, +// \u0026, and \u003c respectively. +// +// This is a global flag and will affect all further gjson operations. +// Ideally, if used, it should be set one time before other gjson functions +// are called. +var DisableEscapeHTML = false + +// AppendJSONString is a convenience function that converts the provided string +// to a valid JSON string and appends it to dst. +func AppendJSONString(dst []byte, s string) []byte { + dst = append(dst, make([]byte, len(s)+2)...) + dst = append(dst[:len(dst)-len(s)-2], '"') + for i := 0; i < len(s); i++ { + if s[i] < ' ' { + dst = append(dst, '\\') + switch s[i] { + case '\b': + dst = append(dst, 'b') + case '\f': + dst = append(dst, 'f') + case '\n': + dst = append(dst, 'n') + case '\r': + dst = append(dst, 'r') + case '\t': + dst = append(dst, 't') + default: + dst = append(dst, 'u') + dst = appendHex16(dst, uint16(s[i])) + } + } else if !DisableEscapeHTML && + (s[i] == '>' || s[i] == '<' || s[i] == '&') { + dst = append(dst, '\\', 'u') + dst = appendHex16(dst, uint16(s[i])) + } else if s[i] == '\\' { + dst = append(dst, '\\', '\\') + } else if s[i] == '"' { + dst = append(dst, '\\', '"') + } else if s[i] > 127 { + // read utf8 character + r, n := utf8.DecodeRuneInString(s[i:]) + if n == 0 { + break + } + if r == utf8.RuneError && n == 1 { + dst = append(dst, `\ufffd`...) + } else if r == '\u2028' || r == '\u2029' { + dst = append(dst, `\u202`...) + dst = append(dst, hexchars[r&0xF]) + } else { + dst = append(dst, s[i:i+n]...) + } + i = i + n - 1 + } else { + dst = append(dst, s[i]) + } + } + return append(dst, '"') +} + +type parseContext struct { + json string + value Result + pipe string + piped bool + calcd bool + lines bool +} + +// Get searches json for the specified path. +// A path is in dot syntax, such as "name.last" or "age". +// When the value is found it's returned immediately. +// +// A path is a series of keys separated by a dot. +// A key may contain special wildcard characters '*' and '?'. +// To access an array value use the index as the key. +// To get the number of elements in an array or to access a child path, use +// the '#' character. +// The dot and wildcard character can be escaped with '\'. +// +// { +// "name": {"first": "Tom", "last": "Anderson"}, +// "age":37, +// "children": ["Sara","Alex","Jack"], +// "friends": [ +// {"first": "James", "last": "Murphy"}, +// {"first": "Roger", "last": "Craig"} +// ] +// } +// "name.last" >> "Anderson" +// "age" >> 37 +// "children" >> ["Sara","Alex","Jack"] +// "children.#" >> 3 +// "children.1" >> "Alex" +// "child*.2" >> "Jack" +// "c?ildren.0" >> "Sara" +// "friends.#.first" >> ["James","Roger"] +// +// This function expects that the json is well-formed, and does not validate. +// Invalid json will not panic, but it may return back unexpected results. +// If you are consuming JSON from an unpredictable source then you may want to +// use the Valid function first. +func Get(json, path string) Result { + if len(path) > 1 { + if (path[0] == '@' && !DisableModifiers) || path[0] == '!' { + // possible modifier + var ok bool + var npath string + var rjson string + if path[0] == '@' && !DisableModifiers { + npath, rjson, ok = execModifier(json, path) + } else if path[0] == '!' { + npath, rjson, ok = execStatic(json, path) + } + if ok { + path = npath + if len(path) > 0 && (path[0] == '|' || path[0] == '.') { + res := Get(rjson, path[1:]) + res.Index = 0 + res.Indexes = nil + return res + } + return Parse(rjson) + } + } + if path[0] == '[' || path[0] == '{' { + // using a subselector path + kind := path[0] + var ok bool + var subs []subSelector + subs, path, ok = parseSubSelectors(path) + if ok { + if len(path) == 0 || (path[0] == '|' || path[0] == '.') { + var b []byte + b = append(b, kind) + var i int + for _, sub := range subs { + res := Get(json, sub.path) + if res.Exists() { + if i > 0 { + b = append(b, ',') + } + if kind == '{' { + if len(sub.name) > 0 { + if sub.name[0] == '"' && Valid(sub.name) { + b = append(b, sub.name...) + } else { + b = AppendJSONString(b, sub.name) + } + } else { + last := nameOfLast(sub.path) + if isSimpleName(last) { + b = AppendJSONString(b, last) + } else { + b = AppendJSONString(b, "_") + } + } + b = append(b, ':') + } + var raw string + if len(res.Raw) == 0 { + raw = res.String() + if len(raw) == 0 { + raw = "null" + } + } else { + raw = res.Raw + } + b = append(b, raw...) + i++ + } + } + b = append(b, kind+2) + var res Result + res.Raw = string(b) + res.Type = JSON + if len(path) > 0 { + res = res.Get(path[1:]) + } + res.Index = 0 + return res + } + } + } + } + var i int + var c = &parseContext{json: json} + if len(path) >= 2 && path[0] == '.' && path[1] == '.' { + c.lines = true + parseArray(c, 0, path[2:]) + } else { + for ; i < len(c.json); i++ { + if c.json[i] == '{' { + i++ + parseObject(c, i, path) + break + } + if c.json[i] == '[' { + i++ + parseArray(c, i, path) + break + } + } + } + if c.piped { + res := c.value.Get(c.pipe) + res.Index = 0 + return res + } + fillIndex(json, c) + return c.value +} + +// GetBytes searches json for the specified path. +// If working with bytes, this method preferred over Get(string(data), path) +func GetBytes(json []byte, path string) Result { + return getBytes(json, path) +} + +// runeit returns the rune from the the \uXXXX +func runeit(json string) rune { + n, _ := strconv.ParseUint(json[:4], 16, 64) + return rune(n) +} + +// unescape unescapes a string +func unescape(json string) string { + var str = make([]byte, 0, len(json)) + for i := 0; i < len(json); i++ { + switch { + default: + str = append(str, json[i]) + case json[i] < ' ': + return string(str) + case json[i] == '\\': + i++ + if i >= len(json) { + return string(str) + } + switch json[i] { + default: + return string(str) + case '\\': + str = append(str, '\\') + case '/': + str = append(str, '/') + case 'b': + str = append(str, '\b') + case 'f': + str = append(str, '\f') + case 'n': + str = append(str, '\n') + case 'r': + str = append(str, '\r') + case 't': + str = append(str, '\t') + case '"': + str = append(str, '"') + case 'u': + if i+5 > len(json) { + return string(str) + } + r := runeit(json[i+1:]) + i += 5 + if utf16.IsSurrogate(r) { + // need another code + if len(json[i:]) >= 6 && json[i] == '\\' && + json[i+1] == 'u' { + // we expect it to be correct so just consume it + r = utf16.DecodeRune(r, runeit(json[i+2:])) + i += 6 + } + } + // provide enough space to encode the largest utf8 possible + str = append(str, 0, 0, 0, 0, 0, 0, 0, 0) + n := utf8.EncodeRune(str[len(str)-8:], r) + str = str[:len(str)-8+n] + i-- // backtrack index by one + } + } + } + return string(str) +} + +// Less return true if a token is less than another token. +// The caseSensitive parameter is used when the tokens are Strings. +// The order when comparing two different type is: +// +// Null < False < Number < String < True < JSON +func (t Result) Less(token Result, caseSensitive bool) bool { + if t.Type < token.Type { + return true + } + if t.Type > token.Type { + return false + } + if t.Type == String { + if caseSensitive { + return t.Str < token.Str + } + return stringLessInsensitive(t.Str, token.Str) + } + if t.Type == Number { + return t.Num < token.Num + } + return t.Raw < token.Raw +} + +func stringLessInsensitive(a, b string) bool { + for i := 0; i < len(a) && i < len(b); i++ { + if a[i] >= 'A' && a[i] <= 'Z' { + if b[i] >= 'A' && b[i] <= 'Z' { + // both are uppercase, do nothing + if a[i] < b[i] { + return true + } else if a[i] > b[i] { + return false + } + } else { + // a is uppercase, convert a to lowercase + if a[i]+32 < b[i] { + return true + } else if a[i]+32 > b[i] { + return false + } + } + } else if b[i] >= 'A' && b[i] <= 'Z' { + // b is uppercase, convert b to lowercase + if a[i] < b[i]+32 { + return true + } else if a[i] > b[i]+32 { + return false + } + } else { + // neither are uppercase + if a[i] < b[i] { + return true + } else if a[i] > b[i] { + return false + } + } + } + return len(a) < len(b) +} + +// parseAny parses the next value from a json string. +// A Result is returned when the hit param is set. +// The return values are (i int, res Result, ok bool) +func parseAny(json string, i int, hit bool) (int, Result, bool) { + var res Result + var val string + for ; i < len(json); i++ { + if json[i] == '{' || json[i] == '[' { + i, val = parseSquash(json, i) + if hit { + res.Raw = val + res.Type = JSON + } + var tmp parseContext + tmp.value = res + fillIndex(json, &tmp) + return i, tmp.value, true + } + if json[i] <= ' ' { + continue + } + var num bool + switch json[i] { + case '"': + i++ + var vesc bool + var ok bool + i, val, vesc, ok = parseString(json, i) + if !ok { + return i, res, false + } + if hit { + res.Type = String + res.Raw = val + if vesc { + res.Str = unescape(val[1 : len(val)-1]) + } else { + res.Str = val[1 : len(val)-1] + } + } + return i, res, true + case 'n': + if i+1 < len(json) && json[i+1] != 'u' { + num = true + break + } + fallthrough + case 't', 'f': + vc := json[i] + i, val = parseLiteral(json, i) + if hit { + res.Raw = val + switch vc { + case 't': + res.Type = True + case 'f': + res.Type = False + } + return i, res, true + } + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + num = true + } + if num { + i, val = parseNumber(json, i) + if hit { + res.Raw = val + res.Type = Number + res.Num, _ = strconv.ParseFloat(val, 64) + } + return i, res, true + } + + } + return i, res, false +} + +// GetMany searches json for the multiple paths. +// The return value is a Result array where the number of items +// will be equal to the number of input paths. +func GetMany(json string, path ...string) []Result { + res := make([]Result, len(path)) + for i, path := range path { + res[i] = Get(json, path) + } + return res +} + +// GetManyBytes searches json for the multiple paths. +// The return value is a Result array where the number of items +// will be equal to the number of input paths. +func GetManyBytes(json []byte, path ...string) []Result { + res := make([]Result, len(path)) + for i, path := range path { + res[i] = GetBytes(json, path) + } + return res +} + +func validpayload(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + i, ok = validany(data, i) + if !ok { + return i, false + } + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + } + } + return i, true + case ' ', '\t', '\n', '\r': + continue + } + } + return i, false +} +func validany(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case '{': + return validobject(data, i+1) + case '[': + return validarray(data, i+1) + case '"': + return validstring(data, i+1) + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return validnumber(data, i+1) + case 't': + return validtrue(data, i+1) + case 'f': + return validfalse(data, i+1) + case 'n': + return validnull(data, i+1) + } + } + return i, false +} +func validobject(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case '}': + return i + 1, true + case '"': + key: + if i, ok = validstring(data, i+1); !ok { + return i, false + } + if i, ok = validcolon(data, i); !ok { + return i, false + } + if i, ok = validany(data, i); !ok { + return i, false + } + if i, ok = validcomma(data, i, '}'); !ok { + return i, false + } + if data[i] == '}' { + return i + 1, true + } + i++ + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case '"': + goto key + } + } + return i, false + } + } + return i, false +} +func validcolon(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case ':': + return i + 1, true + } + } + return i, false +} +func validcomma(data []byte, i int, end byte) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + return i, false + case ' ', '\t', '\n', '\r': + continue + case ',': + return i, true + case end: + return i, true + } + } + return i, false +} +func validarray(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + switch data[i] { + default: + for ; i < len(data); i++ { + if i, ok = validany(data, i); !ok { + return i, false + } + if i, ok = validcomma(data, i, ']'); !ok { + return i, false + } + if data[i] == ']' { + return i + 1, true + } + } + case ' ', '\t', '\n', '\r': + continue + case ']': + return i + 1, true + } + } + return i, false +} +func validstring(data []byte, i int) (outi int, ok bool) { + for ; i < len(data); i++ { + if data[i] < ' ' { + return i, false + } else if data[i] == '\\' { + i++ + if i == len(data) { + return i, false + } + switch data[i] { + default: + return i, false + case '"', '\\', '/', 'b', 'f', 'n', 'r', 't': + case 'u': + for j := 0; j < 4; j++ { + i++ + if i >= len(data) { + return i, false + } + if !((data[i] >= '0' && data[i] <= '9') || + (data[i] >= 'a' && data[i] <= 'f') || + (data[i] >= 'A' && data[i] <= 'F')) { + return i, false + } + } + } + } else if data[i] == '"' { + return i + 1, true + } + } + return i, false +} +func validnumber(data []byte, i int) (outi int, ok bool) { + i-- + // sign + if data[i] == '-' { + i++ + if i == len(data) { + return i, false + } + if data[i] < '0' || data[i] > '9' { + return i, false + } + } + // int + if i == len(data) { + return i, false + } + if data[i] == '0' { + i++ + } else { + for ; i < len(data); i++ { + if data[i] >= '0' && data[i] <= '9' { + continue + } + break + } + } + // frac + if i == len(data) { + return i, true + } + if data[i] == '.' { + i++ + if i == len(data) { + return i, false + } + if data[i] < '0' || data[i] > '9' { + return i, false + } + i++ + for ; i < len(data); i++ { + if data[i] >= '0' && data[i] <= '9' { + continue + } + break + } + } + // exp + if i == len(data) { + return i, true + } + if data[i] == 'e' || data[i] == 'E' { + i++ + if i == len(data) { + return i, false + } + if data[i] == '+' || data[i] == '-' { + i++ + } + if i == len(data) { + return i, false + } + if data[i] < '0' || data[i] > '9' { + return i, false + } + i++ + for ; i < len(data); i++ { + if data[i] >= '0' && data[i] <= '9' { + continue + } + break + } + } + return i, true +} + +func validtrue(data []byte, i int) (outi int, ok bool) { + if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' && + data[i+2] == 'e' { + return i + 3, true + } + return i, false +} +func validfalse(data []byte, i int) (outi int, ok bool) { + if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' && + data[i+2] == 's' && data[i+3] == 'e' { + return i + 4, true + } + return i, false +} +func validnull(data []byte, i int) (outi int, ok bool) { + if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' && + data[i+2] == 'l' { + return i + 3, true + } + return i, false +} + +// Valid returns true if the input is valid json. +// +// if !gjson.Valid(json) { +// return errors.New("invalid json") +// } +// value := gjson.Get(json, "name.last") +func Valid(json string) bool { + _, ok := validpayload(stringBytes(json), 0) + return ok +} + +// ValidBytes returns true if the input is valid json. +// +// if !gjson.Valid(json) { +// return errors.New("invalid json") +// } +// value := gjson.Get(json, "name.last") +// +// If working with bytes, this method preferred over ValidBytes(string(data)) +func ValidBytes(json []byte) bool { + _, ok := validpayload(json, 0) + return ok +} + +func parseUint(s string) (n uint64, ok bool) { + var i int + if i == len(s) { + return 0, false + } + for ; i < len(s); i++ { + if s[i] >= '0' && s[i] <= '9' { + n = n*10 + uint64(s[i]-'0') + } else { + return 0, false + } + } + return n, true +} + +func parseInt(s string) (n int64, ok bool) { + var i int + var sign bool + if len(s) > 0 && s[0] == '-' { + sign = true + i++ + } + if i == len(s) { + return 0, false + } + for ; i < len(s); i++ { + if s[i] >= '0' && s[i] <= '9' { + n = n*10 + int64(s[i]-'0') + } else { + return 0, false + } + } + if sign { + return n * -1, true + } + return n, true +} + +// safeInt validates a given JSON number +// ensures it lies within the minimum and maximum representable JSON numbers +func safeInt(f float64) (n int64, ok bool) { + // https://tc39.es/ecma262/#sec-number.min_safe_integer + // https://tc39.es/ecma262/#sec-number.max_safe_integer + if f < -9007199254740991 || f > 9007199254740991 { + return 0, false + } + return int64(f), true +} + +// execStatic parses the path to find a static value. +// The input expects that the path already starts with a '!' +func execStatic(json, path string) (pathOut, res string, ok bool) { + name := path[1:] + if len(name) > 0 { + switch name[0] { + case '{', '[', '"', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9': + _, res = parseSquash(name, 0) + pathOut = name[len(res):] + return pathOut, res, true + } + } + for i := 1; i < len(path); i++ { + if path[i] == '|' { + pathOut = path[i:] + name = path[1:i] + break + } + if path[i] == '.' { + pathOut = path[i:] + name = path[1:i] + break + } + } + switch strings.ToLower(name) { + case "true", "false", "null", "nan", "inf": + return pathOut, name, true + } + return pathOut, res, false +} + +// execModifier parses the path to find a matching modifier function. +// The input expects that the path already starts with a '@' +func execModifier(json, path string) (pathOut, res string, ok bool) { + name := path[1:] + var hasArgs bool + for i := 1; i < len(path); i++ { + if path[i] == ':' { + pathOut = path[i+1:] + name = path[1:i] + hasArgs = len(pathOut) > 0 + break + } + if path[i] == '|' { + pathOut = path[i:] + name = path[1:i] + break + } + if path[i] == '.' { + pathOut = path[i:] + name = path[1:i] + break + } + } + if fn, ok := modifiers[name]; ok { + var args string + if hasArgs { + var parsedArgs bool + switch pathOut[0] { + case '{', '[', '"': + // json arg + res := Parse(pathOut) + if res.Exists() { + args = squash(pathOut) + pathOut = pathOut[len(args):] + parsedArgs = true + } + } + if !parsedArgs { + // simple arg + i := 0 + for ; i < len(pathOut); i++ { + if pathOut[i] == '|' { + break + } + switch pathOut[i] { + case '{', '[', '"', '(': + s := squash(pathOut[i:]) + i += len(s) - 1 + } + } + args = pathOut[:i] + pathOut = pathOut[i:] + } + } + return pathOut, fn(json, args), true + } + return pathOut, res, false +} + +// unwrap removes the '[]' or '{}' characters around json +func unwrap(json string) string { + json = trim(json) + if len(json) >= 2 && (json[0] == '[' || json[0] == '{') { + json = json[1 : len(json)-1] + } + return json +} + +// DisableModifiers will disable the modifier syntax +var DisableModifiers = false + +var modifiers map[string]func(json, arg string) string + +func init() { + modifiers = map[string]func(json, arg string) string{ + "pretty": modPretty, + "ugly": modUgly, + "reverse": modReverse, + "this": modThis, + "flatten": modFlatten, + "join": modJoin, + "valid": modValid, + "keys": modKeys, + "values": modValues, + "tostr": modToStr, + "fromstr": modFromStr, + "group": modGroup, + "dig": modDig, + } +} + +// AddModifier binds a custom modifier command to the GJSON syntax. +// This operation is not thread safe and should be executed prior to +// using all other gjson function. +func AddModifier(name string, fn func(json, arg string) string) { + modifiers[name] = fn +} + +// ModifierExists returns true when the specified modifier exists. +func ModifierExists(name string, fn func(json, arg string) string) bool { + _, ok := modifiers[name] + return ok +} + +// cleanWS remove any non-whitespace from string +func cleanWS(s string) string { + for i := 0; i < len(s); i++ { + switch s[i] { + case ' ', '\t', '\n', '\r': + continue + default: + var s2 []byte + for i := 0; i < len(s); i++ { + switch s[i] { + case ' ', '\t', '\n', '\r': + s2 = append(s2, s[i]) + } + } + return string(s2) + } + } + return s +} + +// @pretty modifier makes the json look nice. +func modPretty(json, arg string) string { + if len(arg) > 0 { + opts := *pretty.DefaultOptions + Parse(arg).ForEach(func(key, value Result) bool { + switch key.String() { + case "sortKeys": + opts.SortKeys = value.Bool() + case "indent": + opts.Indent = cleanWS(value.String()) + case "prefix": + opts.Prefix = cleanWS(value.String()) + case "width": + opts.Width = int(value.Int()) + } + return true + }) + return bytesString(pretty.PrettyOptions(stringBytes(json), &opts)) + } + return bytesString(pretty.Pretty(stringBytes(json))) +} + +// @this returns the current element. Can be used to retrieve the root element. +func modThis(json, arg string) string { + return json +} + +// @ugly modifier removes all whitespace. +func modUgly(json, arg string) string { + return bytesString(pretty.Ugly(stringBytes(json))) +} + +// @reverse reverses array elements or root object members. +func modReverse(json, arg string) string { + res := Parse(json) + if res.IsArray() { + var values []Result + res.ForEach(func(_, value Result) bool { + values = append(values, value) + return true + }) + out := make([]byte, 0, len(json)) + out = append(out, '[') + for i, j := len(values)-1, 0; i >= 0; i, j = i-1, j+1 { + if j > 0 { + out = append(out, ',') + } + out = append(out, values[i].Raw...) + } + out = append(out, ']') + return bytesString(out) + } + if res.IsObject() { + var keyValues []Result + res.ForEach(func(key, value Result) bool { + keyValues = append(keyValues, key, value) + return true + }) + out := make([]byte, 0, len(json)) + out = append(out, '{') + for i, j := len(keyValues)-2, 0; i >= 0; i, j = i-2, j+1 { + if j > 0 { + out = append(out, ',') + } + out = append(out, keyValues[i+0].Raw...) + out = append(out, ':') + out = append(out, keyValues[i+1].Raw...) + } + out = append(out, '}') + return bytesString(out) + } + return json +} + +// @flatten an array with child arrays. +// +// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]] +// +// The {"deep":true} arg can be provide for deep flattening. +// +// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7] +// +// The original json is returned when the json is not an array. +func modFlatten(json, arg string) string { + res := Parse(json) + if !res.IsArray() { + return json + } + var deep bool + if arg != "" { + Parse(arg).ForEach(func(key, value Result) bool { + if key.String() == "deep" { + deep = value.Bool() + } + return true + }) + } + var out []byte + out = append(out, '[') + var idx int + res.ForEach(func(_, value Result) bool { + var raw string + if value.IsArray() { + if deep { + raw = unwrap(modFlatten(value.Raw, arg)) + } else { + raw = unwrap(value.Raw) + } + } else { + raw = value.Raw + } + raw = strings.TrimSpace(raw) + if len(raw) > 0 { + if idx > 0 { + out = append(out, ',') + } + out = append(out, raw...) + idx++ + } + return true + }) + out = append(out, ']') + return bytesString(out) +} + +// @keys extracts the keys from an object. +// +// {"first":"Tom","last":"Smith"} -> ["first","last"] +func modKeys(json, arg string) string { + v := Parse(json) + if !v.Exists() { + return "[]" + } + obj := v.IsObject() + var out strings.Builder + out.WriteByte('[') + var i int + v.ForEach(func(key, _ Result) bool { + if i > 0 { + out.WriteByte(',') + } + if obj { + out.WriteString(key.Raw) + } else { + out.WriteString("null") + } + i++ + return true + }) + out.WriteByte(']') + return out.String() +} + +// @values extracts the values from an object. +// +// {"first":"Tom","last":"Smith"} -> ["Tom","Smith"] +func modValues(json, arg string) string { + v := Parse(json) + if !v.Exists() { + return "[]" + } + if v.IsArray() { + return json + } + var out strings.Builder + out.WriteByte('[') + var i int + v.ForEach(func(_, value Result) bool { + if i > 0 { + out.WriteByte(',') + } + out.WriteString(value.Raw) + i++ + return true + }) + out.WriteByte(']') + return out.String() +} + +// @join multiple objects into a single object. +// +// [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"} +// +// The arg can be "true" to specify that duplicate keys should be preserved. +// +// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":37,"age":41} +// +// Without preserved keys: +// +// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":41} +// +// The original json is returned when the json is not an object. +func modJoin(json, arg string) string { + res := Parse(json) + if !res.IsArray() { + return json + } + var preserve bool + if arg != "" { + Parse(arg).ForEach(func(key, value Result) bool { + if key.String() == "preserve" { + preserve = value.Bool() + } + return true + }) + } + var out []byte + out = append(out, '{') + if preserve { + // Preserve duplicate keys. + var idx int + res.ForEach(func(_, value Result) bool { + if !value.IsObject() { + return true + } + if idx > 0 { + out = append(out, ',') + } + out = append(out, unwrap(value.Raw)...) + idx++ + return true + }) + } else { + // Deduplicate keys and generate an object with stable ordering. + var keys []Result + kvals := make(map[string]Result) + res.ForEach(func(_, value Result) bool { + if !value.IsObject() { + return true + } + value.ForEach(func(key, value Result) bool { + k := key.String() + if _, ok := kvals[k]; !ok { + keys = append(keys, key) + } + kvals[k] = value + return true + }) + return true + }) + for i := 0; i < len(keys); i++ { + if i > 0 { + out = append(out, ',') + } + out = append(out, keys[i].Raw...) + out = append(out, ':') + out = append(out, kvals[keys[i].String()].Raw...) + } + } + out = append(out, '}') + return bytesString(out) +} + +// @valid ensures that the json is valid before moving on. An empty string is +// returned when the json is not valid, otherwise it returns the original json. +func modValid(json, arg string) string { + if !Valid(json) { + return "" + } + return json +} + +// @fromstr converts a string to json +// +// "{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"} +func modFromStr(json, arg string) string { + if !Valid(json) { + return "" + } + return Parse(json).String() +} + +// @tostr converts a string to json +// +// {"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}" +func modToStr(str, arg string) string { + return string(AppendJSONString(nil, str)) +} + +func modGroup(json, arg string) string { + res := Parse(json) + if !res.IsObject() { + return "" + } + var all [][]byte + res.ForEach(func(key, value Result) bool { + if !value.IsArray() { + return true + } + var idx int + value.ForEach(func(_, value Result) bool { + if idx == len(all) { + all = append(all, []byte{}) + } + all[idx] = append(all[idx], ("," + key.Raw + ":" + value.Raw)...) + idx++ + return true + }) + return true + }) + var data []byte + data = append(data, '[') + for i, item := range all { + if i > 0 { + data = append(data, ',') + } + data = append(data, '{') + data = append(data, item[1:]...) + data = append(data, '}') + } + data = append(data, ']') + return string(data) +} + +// stringHeader instead of reflect.StringHeader +type stringHeader struct { + data unsafe.Pointer + len int +} + +// sliceHeader instead of reflect.SliceHeader +type sliceHeader struct { + data unsafe.Pointer + len int + cap int +} + +// getBytes casts the input json bytes to a string and safely returns the +// results as uniquely allocated data. This operation is intended to minimize +// copies and allocations for the large json string->[]byte. +func getBytes(json []byte, path string) Result { + var result Result + if json != nil { + // unsafe cast to string + result = Get(*(*string)(unsafe.Pointer(&json)), path) + // safely get the string headers + rawhi := *(*stringHeader)(unsafe.Pointer(&result.Raw)) + strhi := *(*stringHeader)(unsafe.Pointer(&result.Str)) + // create byte slice headers + rawh := sliceHeader{data: rawhi.data, len: rawhi.len, cap: rawhi.len} + strh := sliceHeader{data: strhi.data, len: strhi.len, cap: rawhi.len} + if strh.data == nil { + // str is nil + if rawh.data == nil { + // raw is nil + result.Raw = "" + } else { + // raw has data, safely copy the slice header to a string + result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) + } + result.Str = "" + } else if rawh.data == nil { + // raw is nil + result.Raw = "" + // str has data, safely copy the slice header to a string + result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) + } else if uintptr(strh.data) >= uintptr(rawh.data) && + uintptr(strh.data)+uintptr(strh.len) <= + uintptr(rawh.data)+uintptr(rawh.len) { + // Str is a substring of Raw. + start := uintptr(strh.data) - uintptr(rawh.data) + // safely copy the raw slice header + result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) + // substring the raw + result.Str = result.Raw[start : start+uintptr(strh.len)] + } else { + // safely copy both the raw and str slice headers to strings + result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) + result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) + } + } + return result +} + +// fillIndex finds the position of Raw data and assigns it to the Index field +// of the resulting value. If the position cannot be found then Index zero is +// used instead. +func fillIndex(json string, c *parseContext) { + if len(c.value.Raw) > 0 && !c.calcd { + jhdr := *(*stringHeader)(unsafe.Pointer(&json)) + rhdr := *(*stringHeader)(unsafe.Pointer(&(c.value.Raw))) + c.value.Index = int(uintptr(rhdr.data) - uintptr(jhdr.data)) + if c.value.Index < 0 || c.value.Index >= len(json) { + c.value.Index = 0 + } + } +} + +func stringBytes(s string) []byte { + return *(*[]byte)(unsafe.Pointer(&sliceHeader{ + data: (*stringHeader)(unsafe.Pointer(&s)).data, + len: len(s), + cap: len(s), + })) +} + +func bytesString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +func revSquash(json string) string { + // reverse squash + // expects that the tail character is a ']' or '}' or ')' or '"' + // squash the value, ignoring all nested arrays and objects. + i := len(json) - 1 + var depth int + if json[i] != '"' { + depth++ + } + if json[i] == '}' || json[i] == ']' || json[i] == ')' { + i-- + } + for ; i >= 0; i-- { + switch json[i] { + case '"': + i-- + for ; i >= 0; i-- { + if json[i] == '"' { + esc := 0 + for i > 0 && json[i-1] == '\\' { + i-- + esc++ + } + if esc%2 == 1 { + continue + } + i += esc + break + } + } + if depth == 0 { + if i < 0 { + i = 0 + } + return json[i:] + } + case '}', ']', ')': + depth++ + case '{', '[', '(': + depth-- + if depth == 0 { + return json[i:] + } + } + } + return json +} + +// Paths returns the original GJSON paths for a Result where the Result came +// from a simple query path that returns an array, like: +// +// gjson.Get(json, "friends.#.first") +// +// The returned value will be in the form of a JSON array: +// +// ["friends.0.first","friends.1.first","friends.2.first"] +// +// The param 'json' must be the original JSON used when calling Get. +// +// Returns an empty string if the paths cannot be determined, which can happen +// when the Result came from a path that contained a multipath, modifier, +// or a nested query. +func (t Result) Paths(json string) []string { + if t.Indexes == nil { + return nil + } + paths := make([]string, 0, len(t.Indexes)) + t.ForEach(func(_, value Result) bool { + paths = append(paths, value.Path(json)) + return true + }) + if len(paths) != len(t.Indexes) { + return nil + } + return paths +} + +// Path returns the original GJSON path for a Result where the Result came +// from a simple path that returns a single value, like: +// +// gjson.Get(json, "friends.#(last=Murphy)") +// +// The returned value will be in the form of a JSON string: +// +// "friends.0" +// +// The param 'json' must be the original JSON used when calling Get. +// +// Returns an empty string if the paths cannot be determined, which can happen +// when the Result came from a path that contained a multipath, modifier, +// or a nested query. +func (t Result) Path(json string) string { + var path []byte + var comps []string // raw components + i := t.Index - 1 + if t.Index+len(t.Raw) > len(json) { + // JSON cannot safely contain Result. + goto fail + } + if !strings.HasPrefix(json[t.Index:], t.Raw) { + // Result is not at the JSON index as expected. + goto fail + } + for ; i >= 0; i-- { + if json[i] <= ' ' { + continue + } + if json[i] == ':' { + // inside of object, get the key + for ; i >= 0; i-- { + if json[i] != '"' { + continue + } + break + } + raw := revSquash(json[:i+1]) + i = i - len(raw) + comps = append(comps, raw) + // key gotten, now squash the rest + raw = revSquash(json[:i+1]) + i = i - len(raw) + i++ // increment the index for next loop step + } else if json[i] == '{' { + // Encountered an open object. The original result was probably an + // object key. + goto fail + } else if json[i] == ',' || json[i] == '[' { + // inside of an array, count the position + var arrIdx int + if json[i] == ',' { + arrIdx++ + i-- + } + for ; i >= 0; i-- { + if json[i] == ':' { + // Encountered an unexpected colon. The original result was + // probably an object key. + goto fail + } else if json[i] == ',' { + arrIdx++ + } else if json[i] == '[' { + comps = append(comps, strconv.Itoa(arrIdx)) + break + } else if json[i] == ']' || json[i] == '}' || json[i] == '"' { + raw := revSquash(json[:i+1]) + i = i - len(raw) + 1 + } + } + } + } + if len(comps) == 0 { + if DisableModifiers { + goto fail + } + return "@this" + } + for i := len(comps) - 1; i >= 0; i-- { + rcomp := Parse(comps[i]) + if !rcomp.Exists() { + goto fail + } + comp := Escape(rcomp.String()) + path = append(path, '.') + path = append(path, comp...) + } + if len(path) > 0 { + path = path[1:] + } + return string(path) +fail: + return "" +} + +// isSafePathKeyChar returns true if the input character is safe for not +// needing escaping. +func isSafePathKeyChar(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' || + c == '-' || c == ':' +} + +// Escape returns an escaped path component. +// +// json := `{ +// "user":{ +// "first.name": "Janet", +// "last.name": "Prichard" +// } +// }` +// user := gjson.Get(json, "user") +// println(user.Get(gjson.Escape("first.name")) +// println(user.Get(gjson.Escape("last.name")) +// // Output: +// // Janet +// // Prichard +func Escape(comp string) string { + for i := 0; i < len(comp); i++ { + if !isSafePathKeyChar(comp[i]) { + ncomp := make([]byte, len(comp)+1) + copy(ncomp, comp[:i]) + ncomp = ncomp[:i] + for ; i < len(comp); i++ { + if !isSafePathKeyChar(comp[i]) { + ncomp = append(ncomp, '\\') + } + ncomp = append(ncomp, comp[i]) + } + return string(ncomp) + } + } + return comp +} + +func parseRecursiveDescent(all []Result, parent Result, path string) []Result { + if res := parent.Get(path); res.Exists() { + all = append(all, res) + } + if parent.IsArray() || parent.IsObject() { + parent.ForEach(func(_, val Result) bool { + all = parseRecursiveDescent(all, val, path) + return true + }) + } + return all +} + +func modDig(json, arg string) string { + all := parseRecursiveDescent(nil, Parse(json), arg) + var out []byte + out = append(out, '[') + for i, res := range all { + if i > 0 { + out = append(out, ',') + } + out = append(out, res.Raw...) + } + out = append(out, ']') + return string(out) +} diff --git a/vendor/github.com/tidwall/match/LICENSE b/vendor/github.com/tidwall/match/LICENSE new file mode 100644 index 00000000..58f5819a --- /dev/null +++ b/vendor/github.com/tidwall/match/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/tidwall/match/README.md b/vendor/github.com/tidwall/match/README.md new file mode 100644 index 00000000..9134079e --- /dev/null +++ b/vendor/github.com/tidwall/match/README.md @@ -0,0 +1,29 @@ +# Match + +[![GoDoc](https://godoc.org/github.com/tidwall/match?status.svg)](https://godoc.org/github.com/tidwall/match) + +Match is a very simple pattern matcher where '*' matches on any +number characters and '?' matches on any one character. + +## Installing + +``` +go get -u github.com/tidwall/match +``` + +## Example + +```go +match.Match("hello", "*llo") +match.Match("jello", "?ello") +match.Match("hello", "h*o") +``` + + +## Contact + +Josh Baker [@tidwall](http://twitter.com/tidwall) + +## License + +Match source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/match/match.go b/vendor/github.com/tidwall/match/match.go new file mode 100644 index 00000000..d855a03f --- /dev/null +++ b/vendor/github.com/tidwall/match/match.go @@ -0,0 +1,273 @@ +// Package match provides a simple pattern matcher with unicode support. +package match + +import ( + "unicode/utf8" +) + +// Match returns true if str matches pattern. This is a very +// simple wildcard match where '*' matches on any number characters +// and '?' matches on any one character. +// +// pattern: +// +// { term } +// +// term: +// +// '*' matches any sequence of non-Separator characters +// '?' matches any single non-Separator character +// c matches character c (c != '*', '?', '\\') +// '\\' c matches character c +func Match(str, pattern string) bool { + return match0(str, pattern, false) +} + +// MatchNoCase is the same as Match but performs a case-insensitive match. +// Such that string "Hello World" with match with lower case pattern "hello*" +func MatchNoCase(str, pattern string) bool { + return match0(str, pattern, true) +} + +func match0(str, pattern string, nocase bool) bool { + if pattern == "*" { + return true + } + return match(str, pattern, 0, nil, -1, nocase) == rMatch +} + +// MatchLimit is the same as Match but will limit the complexity of the match +// operation. This is to avoid long running matches, specifically to avoid ReDos +// attacks from arbritary inputs. +// +// How it works: +// The underlying match routine is recursive and may call itself when it +// encounters a sandwiched wildcard pattern, such as: `user:*:name`. +// Everytime it calls itself a counter is incremented. +// The operation is stopped when counter > maxcomp*len(str). +func MatchLimit(str, pattern string, maxcomp int) (matched, stopped bool) { + return matchLimit0(str, pattern, maxcomp, false) +} + +func MatchLimitNoCase(str, pattern string, maxcomp int, +) (matched, stopped bool) { + return matchLimit0(str, pattern, maxcomp, true) +} + +func matchLimit0(str, pattern string, maxcomp int, nocase bool, +) (matched, stopped bool) { + if pattern == "*" { + return true, false + } + counter := 0 + r := match(str, pattern, len(str), &counter, maxcomp, nocase) + if r == rStop { + return false, true + } + return r == rMatch, false +} + +type result int + +const ( + rNoMatch result = iota + rMatch + rStop +) + +func tolower(r rune) rune { + if r >= 'A' && r <= 'Z' { + return r + 32 + } + return r +} + +func match(str, pat string, slen int, counter *int, maxcomp int, nocase bool, +) result { + // check complexity limit + if maxcomp > -1 { + if *counter > slen*maxcomp { + return rStop + } + *counter++ + } + + for len(pat) > 0 { + var wild bool + pc, ps := rune(pat[0]), 1 + if pc > 0x7f { + pc, ps = utf8.DecodeRuneInString(pat) + } + var sc rune + var ss int + if len(str) > 0 { + sc, ss = rune(str[0]), 1 + if sc > 0x7f { + sc, ss = utf8.DecodeRuneInString(str) + } + } + switch pc { + case '?': + if ss == 0 { + return rNoMatch + } + case '*': + // Ignore repeating stars. + for len(pat) > 1 && pat[1] == '*' { + pat = pat[1:] + } + + // If this star is the last character then it must be a match. + if len(pat) == 1 { + return rMatch + } + + // Match and trim any non-wildcard suffix characters. + var ok bool + str, pat, ok = matchTrimSuffix(str, pat, nocase) + if !ok { + return rNoMatch + } + + // Check for single star again. + if len(pat) == 1 { + return rMatch + } + + // Perform recursive wildcard search. + r := match(str, pat[1:], slen, counter, maxcomp, nocase) + if r != rNoMatch { + return r + } + if len(str) == 0 { + return rNoMatch + } + wild = true + default: + if ss == 0 { + return rNoMatch + } + if pc == '\\' { + pat = pat[ps:] + pc, ps = utf8.DecodeRuneInString(pat) + if ps == 0 { + return rNoMatch + } + } + if nocase { + sc, pc = tolower(sc), tolower(pc) + } + if sc != pc { + return rNoMatch + } + } + str = str[ss:] + if !wild { + pat = pat[ps:] + } + } + if len(str) == 0 { + return rMatch + } + return rNoMatch +} + +// matchTrimSuffix matches and trims any non-wildcard suffix characters. +// Returns the trimed string and pattern. +// +// This is called because the pattern contains extra data after the wildcard +// star. Here we compare any suffix characters in the pattern to the suffix of +// the target string. Basically a reverse match that stops when a wildcard +// character is reached. This is a little trickier than a forward match because +// we need to evaluate an escaped character in reverse. +// +// Any matched characters will be trimmed from both the target +// string and the pattern. +func matchTrimSuffix(str, pat string, nocase bool) (string, string, bool) { + // It's expected that the pattern has at least two bytes and the first byte + // is a wildcard star '*' + match := true + for len(str) > 0 && len(pat) > 1 { + pc, ps := utf8.DecodeLastRuneInString(pat) + var esc bool + for i := 0; ; i++ { + if pat[len(pat)-ps-i-1] != '\\' { + if i&1 == 1 { + esc = true + ps++ + } + break + } + } + if pc == '*' && !esc { + match = true + break + } + sc, ss := utf8.DecodeLastRuneInString(str) + if nocase { + pc, sc = tolower(pc), tolower(sc) + } + if !((pc == '?' && !esc) || pc == sc) { + match = false + break + } + str = str[:len(str)-ss] + pat = pat[:len(pat)-ps] + } + return str, pat, match +} + +var maxRuneBytes = [...]byte{244, 143, 191, 191} + +// Allowable parses the pattern and determines the minimum and maximum allowable +// values that the pattern can represent. +// When the max cannot be determined, 'true' will be returned +// for infinite. +func Allowable(pattern string) (min, max string) { + if pattern == "" || pattern[0] == '*' { + return "", "" + } + + minb := make([]byte, 0, len(pattern)) + maxb := make([]byte, 0, len(pattern)) + var wild bool + for i := 0; i < len(pattern); i++ { + if pattern[i] == '*' { + wild = true + break + } + if pattern[i] == '?' { + minb = append(minb, 0) + maxb = append(maxb, maxRuneBytes[:]...) + } else { + minb = append(minb, pattern[i]) + maxb = append(maxb, pattern[i]) + } + } + if wild { + r, n := utf8.DecodeLastRune(maxb) + if r != utf8.RuneError { + if r < utf8.MaxRune { + r++ + if r > 0x7f { + b := make([]byte, 4) + nn := utf8.EncodeRune(b, r) + maxb = append(maxb[:len(maxb)-n], b[:nn]...) + } else { + maxb = append(maxb[:len(maxb)-n], byte(r)) + } + } + } + } + return string(minb), string(maxb) +} + +// IsPattern returns true if the string is a pattern. +func IsPattern(str string) bool { + for i := 0; i < len(str); i++ { + if str[i] == '*' || str[i] == '?' { + return true + } + } + return false +} diff --git a/vendor/github.com/tidwall/sjson/LICENSE b/vendor/github.com/tidwall/sjson/LICENSE new file mode 100644 index 00000000..89593c7c --- /dev/null +++ b/vendor/github.com/tidwall/sjson/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/tidwall/sjson/README.md b/vendor/github.com/tidwall/sjson/README.md new file mode 100644 index 00000000..4598424e --- /dev/null +++ b/vendor/github.com/tidwall/sjson/README.md @@ -0,0 +1,278 @@ +

+SJSON +
+GoDoc +

+ +

set a json value quickly

+ +SJSON is a Go package that provides a [very fast](#performance) and simple way to set a value in a json document. +For quickly retrieving json values check out [GJSON](https://github.com/tidwall/gjson). + +For a command line interface check out [JJ](https://github.com/tidwall/jj). + +Getting Started +=============== + +Installing +---------- + +To start using SJSON, install Go and run `go get`: + +```sh +$ go get -u github.com/tidwall/sjson +``` + +This will retrieve the library. + +Set a value +----------- +Set sets the value for the specified path. +A path is in dot syntax, such as "name.last" or "age". +This function expects that the json is well-formed and validated. +Invalid json will not panic, but it may return back unexpected results. +Invalid paths may return an error. + +```go +package main + +import "github.com/tidwall/sjson" + +const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}` + +func main() { + value, _ := sjson.Set(json, "name.last", "Anderson") + println(value) +} +``` + +This will print: + +```json +{"name":{"first":"Janet","last":"Anderson"},"age":47} +``` + +Path syntax +----------- + +A path is a series of keys separated by a dot. +The dot and colon characters can be escaped with ``\``. + +```json +{ + "name": {"first": "Tom", "last": "Anderson"}, + "age":37, + "children": ["Sara","Alex","Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + {"first": "James", "last": "Murphy"}, + {"first": "Roger", "last": "Craig"} + ] +} +``` +``` +"name.last" >> "Anderson" +"age" >> 37 +"children.1" >> "Alex" +"friends.1.last" >> "Craig" +``` + +The `-1` key can be used to append a value to an existing array: + +``` +"children.-1" >> appends a new value to the end of the children array +``` + +Normally number keys are used to modify arrays, but it's possible to force a numeric object key by using the colon character: + +```json +{ + "users":{ + "2313":{"name":"Sara"}, + "7839":{"name":"Andy"} + } +} +``` + +A colon path would look like: + +``` +"users.:2313.name" >> "Sara" +``` + +Supported types +--------------- + +Pretty much any type is supported: + +```go +sjson.Set(`{"key":true}`, "key", nil) +sjson.Set(`{"key":true}`, "key", false) +sjson.Set(`{"key":true}`, "key", 1) +sjson.Set(`{"key":true}`, "key", 10.5) +sjson.Set(`{"key":true}`, "key", "hello") +sjson.Set(`{"key":true}`, "key", []string{"hello", "world"}) +sjson.Set(`{"key":true}`, "key", map[string]interface{}{"hello":"world"}) +``` + +When a type is not recognized, SJSON will fallback to the `encoding/json` Marshaller. + + +Examples +-------- + +Set a value from empty document: +```go +value, _ := sjson.Set("", "name", "Tom") +println(value) + +// Output: +// {"name":"Tom"} +``` + +Set a nested value from empty document: +```go +value, _ := sjson.Set("", "name.last", "Anderson") +println(value) + +// Output: +// {"name":{"last":"Anderson"}} +``` + +Set a new value: +```go +value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.first", "Sara") +println(value) + +// Output: +// {"name":{"first":"Sara","last":"Anderson"}} +``` + +Update an existing value: +```go +value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.last", "Smith") +println(value) + +// Output: +// {"name":{"last":"Smith"}} +``` + +Set a new array value: +```go +value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.2", "Sara") +println(value) + +// Output: +// {"friends":["Andy","Carol","Sara"] +``` + +Append an array value by using the `-1` key in a path: +```go +value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.-1", "Sara") +println(value) + +// Output: +// {"friends":["Andy","Carol","Sara"] +``` + +Append an array value that is past the end: +```go +value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.4", "Sara") +println(value) + +// Output: +// {"friends":["Andy","Carol",null,null,"Sara"] +``` + +Delete a value: +```go +value, _ := sjson.Delete(`{"name":{"first":"Sara","last":"Anderson"}}`, "name.first") +println(value) + +// Output: +// {"name":{"last":"Anderson"}} +``` + +Delete an array value: +```go +value, _ := sjson.Delete(`{"friends":["Andy","Carol"]}`, "friends.1") +println(value) + +// Output: +// {"friends":["Andy"]} +``` + +Delete the last array value: +```go +value, _ := sjson.Delete(`{"friends":["Andy","Carol"]}`, "friends.-1") +println(value) + +// Output: +// {"friends":["Andy"]} +``` + +## Performance + +Benchmarks of SJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), +[ffjson](https://github.com/pquerna/ffjson), +[EasyJSON](https://github.com/mailru/easyjson), +and [Gabs](https://github.com/Jeffail/gabs) + +``` +Benchmark_SJSON-8 3000000 805 ns/op 1077 B/op 3 allocs/op +Benchmark_SJSON_ReplaceInPlace-8 3000000 449 ns/op 0 B/op 0 allocs/op +Benchmark_JSON_Map-8 300000 21236 ns/op 6392 B/op 150 allocs/op +Benchmark_JSON_Struct-8 300000 14691 ns/op 1789 B/op 24 allocs/op +Benchmark_Gabs-8 300000 21311 ns/op 6752 B/op 150 allocs/op +Benchmark_FFJSON-8 300000 17673 ns/op 3589 B/op 47 allocs/op +Benchmark_EasyJSON-8 1500000 3119 ns/op 1061 B/op 13 allocs/op +``` + +JSON document used: + +```json +{ + "widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + } +} +``` + +Each operation was rotated though one of the following search paths: + +``` +widget.window.name +widget.image.hOffset +widget.text.onMouseUp +``` + +*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7 and can be be found [here](https://github.com/tidwall/sjson-benchmarks)*. + +## Contact +Josh Baker [@tidwall](http://twitter.com/tidwall) + +## License + +SJSON source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/sjson/logo.png b/vendor/github.com/tidwall/sjson/logo.png new file mode 100644 index 00000000..b5aa257b Binary files /dev/null and b/vendor/github.com/tidwall/sjson/logo.png differ diff --git a/vendor/github.com/tidwall/sjson/sjson.go b/vendor/github.com/tidwall/sjson/sjson.go new file mode 100644 index 00000000..a55eef3f --- /dev/null +++ b/vendor/github.com/tidwall/sjson/sjson.go @@ -0,0 +1,737 @@ +// Package sjson provides setting json values. +package sjson + +import ( + jsongo "encoding/json" + "sort" + "strconv" + "unsafe" + + "github.com/tidwall/gjson" +) + +type errorType struct { + msg string +} + +func (err *errorType) Error() string { + return err.msg +} + +// Options represents additional options for the Set and Delete functions. +type Options struct { + // Optimistic is a hint that the value likely exists which + // allows for the sjson to perform a fast-track search and replace. + Optimistic bool + // ReplaceInPlace is a hint to replace the input json rather than + // allocate a new json byte slice. When this field is specified + // the input json will not longer be valid and it should not be used + // In the case when the destination slice doesn't have enough free + // bytes to replace the data in place, a new bytes slice will be + // created under the hood. + // The Optimistic flag must be set to true and the input must be a + // byte slice in order to use this field. + ReplaceInPlace bool +} + +type pathResult struct { + part string // current key part + gpart string // gjson get part + path string // remaining path + force bool // force a string key + more bool // there is more path to parse +} + +func isSimpleChar(ch byte) bool { + switch ch { + case '|', '#', '@', '*', '?': + return false + default: + return true + } +} + +func parsePath(path string) (res pathResult, simple bool) { + var r pathResult + if len(path) > 0 && path[0] == ':' { + r.force = true + path = path[1:] + } + for i := 0; i < len(path); i++ { + if path[i] == '.' { + r.part = path[:i] + r.gpart = path[:i] + r.path = path[i+1:] + r.more = true + return r, true + } + if !isSimpleChar(path[i]) { + return r, false + } + if path[i] == '\\' { + // go into escape mode. this is a slower path that + // strips off the escape character from the part. + epart := []byte(path[:i]) + gpart := []byte(path[:i+1]) + i++ + if i < len(path) { + epart = append(epart, path[i]) + gpart = append(gpart, path[i]) + i++ + for ; i < len(path); i++ { + if path[i] == '\\' { + gpart = append(gpart, '\\') + i++ + if i < len(path) { + epart = append(epart, path[i]) + gpart = append(gpart, path[i]) + } + continue + } else if path[i] == '.' { + r.part = string(epart) + r.gpart = string(gpart) + r.path = path[i+1:] + r.more = true + return r, true + } else if !isSimpleChar(path[i]) { + return r, false + } + epart = append(epart, path[i]) + gpart = append(gpart, path[i]) + } + } + // append the last part + r.part = string(epart) + r.gpart = string(gpart) + return r, true + } + } + r.part = path + r.gpart = path + return r, true +} + +func mustMarshalString(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] < ' ' || s[i] > 0x7f || s[i] == '"' || s[i] == '\\' { + return true + } + } + return false +} + +// appendStringify makes a json string and appends to buf. +func appendStringify(buf []byte, s string) []byte { + if mustMarshalString(s) { + b, _ := jsongo.Marshal(s) + return append(buf, b...) + } + buf = append(buf, '"') + buf = append(buf, s...) + buf = append(buf, '"') + return buf +} + +// appendBuild builds a json block from a json path. +func appendBuild(buf []byte, array bool, paths []pathResult, raw string, + stringify bool) []byte { + if !array { + buf = appendStringify(buf, paths[0].part) + buf = append(buf, ':') + } + if len(paths) > 1 { + n, numeric := atoui(paths[1]) + if numeric || (!paths[1].force && paths[1].part == "-1") { + buf = append(buf, '[') + buf = appendRepeat(buf, "null,", n) + buf = appendBuild(buf, true, paths[1:], raw, stringify) + buf = append(buf, ']') + } else { + buf = append(buf, '{') + buf = appendBuild(buf, false, paths[1:], raw, stringify) + buf = append(buf, '}') + } + } else { + if stringify { + buf = appendStringify(buf, raw) + } else { + buf = append(buf, raw...) + } + } + return buf +} + +// atoui does a rip conversion of string -> unigned int. +func atoui(r pathResult) (n int, ok bool) { + if r.force { + return 0, false + } + for i := 0; i < len(r.part); i++ { + if r.part[i] < '0' || r.part[i] > '9' { + return 0, false + } + n = n*10 + int(r.part[i]-'0') + } + return n, true +} + +// appendRepeat repeats string "n" times and appends to buf. +func appendRepeat(buf []byte, s string, n int) []byte { + for i := 0; i < n; i++ { + buf = append(buf, s...) + } + return buf +} + +// trim does a rip trim +func trim(s string) string { + for len(s) > 0 { + if s[0] <= ' ' { + s = s[1:] + continue + } + break + } + for len(s) > 0 { + if s[len(s)-1] <= ' ' { + s = s[:len(s)-1] + continue + } + break + } + return s +} + +// deleteTailItem deletes the previous key or comma. +func deleteTailItem(buf []byte) ([]byte, bool) { +loop: + for i := len(buf) - 1; i >= 0; i-- { + // look for either a ',',':','[' + switch buf[i] { + case '[': + return buf, true + case ',': + return buf[:i], false + case ':': + // delete tail string + i-- + for ; i >= 0; i-- { + if buf[i] == '"' { + i-- + for ; i >= 0; i-- { + if buf[i] == '"' { + i-- + if i >= 0 && buf[i] == '\\' { + i-- + continue + } + for ; i >= 0; i-- { + // look for either a ',','{' + switch buf[i] { + case '{': + return buf[:i+1], true + case ',': + return buf[:i], false + } + } + } + } + break + } + } + break loop + } + } + return buf, false +} + +var errNoChange = &errorType{"no change"} + +func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string, + stringify, del bool) ([]byte, error) { + var err error + var res gjson.Result + var found bool + if del { + if paths[0].part == "-1" && !paths[0].force { + res = gjson.Get(jstr, "#") + if res.Int() > 0 { + res = gjson.Get(jstr, strconv.FormatInt(int64(res.Int()-1), 10)) + found = true + } + } + } + if !found { + res = gjson.Get(jstr, paths[0].gpart) + } + if res.Index > 0 { + if len(paths) > 1 { + buf = append(buf, jstr[:res.Index]...) + buf, err = appendRawPaths(buf, res.Raw, paths[1:], raw, + stringify, del) + if err != nil { + return nil, err + } + buf = append(buf, jstr[res.Index+len(res.Raw):]...) + return buf, nil + } + buf = append(buf, jstr[:res.Index]...) + var exidx int // additional forward stripping + if del { + var delNextComma bool + buf, delNextComma = deleteTailItem(buf) + if delNextComma { + i, j := res.Index+len(res.Raw), 0 + for ; i < len(jstr); i, j = i+1, j+1 { + if jstr[i] <= ' ' { + continue + } + if jstr[i] == ',' { + exidx = j + 1 + } + break + } + } + } else { + if stringify { + buf = appendStringify(buf, raw) + } else { + buf = append(buf, raw...) + } + } + buf = append(buf, jstr[res.Index+len(res.Raw)+exidx:]...) + return buf, nil + } + if del { + return nil, errNoChange + } + n, numeric := atoui(paths[0]) + isempty := true + for i := 0; i < len(jstr); i++ { + if jstr[i] > ' ' { + isempty = false + break + } + } + if isempty { + if numeric { + jstr = "[]" + } else { + jstr = "{}" + } + } + jsres := gjson.Parse(jstr) + if jsres.Type != gjson.JSON { + if numeric { + jstr = "[]" + } else { + jstr = "{}" + } + jsres = gjson.Parse(jstr) + } + var comma bool + for i := 1; i < len(jsres.Raw); i++ { + if jsres.Raw[i] <= ' ' { + continue + } + if jsres.Raw[i] == '}' || jsres.Raw[i] == ']' { + break + } + comma = true + break + } + switch jsres.Raw[0] { + default: + return nil, &errorType{"json must be an object or array"} + case '{': + end := len(jsres.Raw) - 1 + for ; end > 0; end-- { + if jsres.Raw[end] == '}' { + break + } + } + buf = append(buf, jsres.Raw[:end]...) + if comma { + buf = append(buf, ',') + } + buf = appendBuild(buf, false, paths, raw, stringify) + buf = append(buf, '}') + return buf, nil + case '[': + var appendit bool + if !numeric { + if paths[0].part == "-1" && !paths[0].force { + appendit = true + } else { + return nil, &errorType{ + "cannot set array element for non-numeric key '" + + paths[0].part + "'"} + } + } + if appendit { + njson := trim(jsres.Raw) + if njson[len(njson)-1] == ']' { + njson = njson[:len(njson)-1] + } + buf = append(buf, njson...) + if comma { + buf = append(buf, ',') + } + + buf = appendBuild(buf, true, paths, raw, stringify) + buf = append(buf, ']') + return buf, nil + } + buf = append(buf, '[') + ress := jsres.Array() + for i := 0; i < len(ress); i++ { + if i > 0 { + buf = append(buf, ',') + } + buf = append(buf, ress[i].Raw...) + } + if len(ress) == 0 { + buf = appendRepeat(buf, "null,", n-len(ress)) + } else { + buf = appendRepeat(buf, ",null", n-len(ress)) + if comma { + buf = append(buf, ',') + } + } + buf = appendBuild(buf, true, paths, raw, stringify) + buf = append(buf, ']') + return buf, nil + } +} + +func isOptimisticPath(path string) bool { + for i := 0; i < len(path); i++ { + if path[i] < '.' || path[i] > 'z' { + return false + } + if path[i] > '9' && path[i] < 'A' { + return false + } + if path[i] > 'z' { + return false + } + } + return true +} + +// Set sets a json value for the specified path. +// A path is in dot syntax, such as "name.last" or "age". +// This function expects that the json is well-formed, and does not validate. +// Invalid json will not panic, but it may return back unexpected results. +// An error is returned if the path is not valid. +// +// A path is a series of keys separated by a dot. +// +// { +// "name": {"first": "Tom", "last": "Anderson"}, +// "age":37, +// "children": ["Sara","Alex","Jack"], +// "friends": [ +// {"first": "James", "last": "Murphy"}, +// {"first": "Roger", "last": "Craig"} +// ] +// } +// "name.last" >> "Anderson" +// "age" >> 37 +// "children.1" >> "Alex" +// +func Set(json, path string, value interface{}) (string, error) { + return SetOptions(json, path, value, nil) +} + +// SetBytes sets a json value for the specified path. +// If working with bytes, this method preferred over +// Set(string(data), path, value) +func SetBytes(json []byte, path string, value interface{}) ([]byte, error) { + return SetBytesOptions(json, path, value, nil) +} + +// SetRaw sets a raw json value for the specified path. +// This function works the same as Set except that the value is set as a +// raw block of json. This allows for setting premarshalled json objects. +func SetRaw(json, path, value string) (string, error) { + return SetRawOptions(json, path, value, nil) +} + +// SetRawOptions sets a raw json value for the specified path with options. +// This furnction works the same as SetOptions except that the value is set +// as a raw block of json. This allows for setting premarshalled json objects. +func SetRawOptions(json, path, value string, opts *Options) (string, error) { + var optimistic bool + if opts != nil { + optimistic = opts.Optimistic + } + res, err := set(json, path, value, false, false, optimistic, false) + if err == errNoChange { + return json, nil + } + return string(res), err +} + +// SetRawBytes sets a raw json value for the specified path. +// If working with bytes, this method preferred over +// SetRaw(string(data), path, value) +func SetRawBytes(json []byte, path string, value []byte) ([]byte, error) { + return SetRawBytesOptions(json, path, value, nil) +} + +type dtype struct{} + +// Delete deletes a value from json for the specified path. +func Delete(json, path string) (string, error) { + return Set(json, path, dtype{}) +} + +// DeleteBytes deletes a value from json for the specified path. +func DeleteBytes(json []byte, path string) ([]byte, error) { + return SetBytes(json, path, dtype{}) +} + +type stringHeader struct { + data unsafe.Pointer + len int +} + +type sliceHeader struct { + data unsafe.Pointer + len int + cap int +} + +func set(jstr, path, raw string, + stringify, del, optimistic, inplace bool) ([]byte, error) { + if path == "" { + return []byte(jstr), &errorType{"path cannot be empty"} + } + if !del && optimistic && isOptimisticPath(path) { + res := gjson.Get(jstr, path) + if res.Exists() && res.Index > 0 { + sz := len(jstr) - len(res.Raw) + len(raw) + if stringify { + sz += 2 + } + if inplace && sz <= len(jstr) { + if !stringify || !mustMarshalString(raw) { + jsonh := *(*stringHeader)(unsafe.Pointer(&jstr)) + jsonbh := sliceHeader{ + data: jsonh.data, len: jsonh.len, cap: jsonh.len} + jbytes := *(*[]byte)(unsafe.Pointer(&jsonbh)) + if stringify { + jbytes[res.Index] = '"' + copy(jbytes[res.Index+1:], []byte(raw)) + jbytes[res.Index+1+len(raw)] = '"' + copy(jbytes[res.Index+1+len(raw)+1:], + jbytes[res.Index+len(res.Raw):]) + } else { + copy(jbytes[res.Index:], []byte(raw)) + copy(jbytes[res.Index+len(raw):], + jbytes[res.Index+len(res.Raw):]) + } + return jbytes[:sz], nil + } + return []byte(jstr), nil + } + buf := make([]byte, 0, sz) + buf = append(buf, jstr[:res.Index]...) + if stringify { + buf = appendStringify(buf, raw) + } else { + buf = append(buf, raw...) + } + buf = append(buf, jstr[res.Index+len(res.Raw):]...) + return buf, nil + } + } + var paths []pathResult + r, simple := parsePath(path) + if simple { + paths = append(paths, r) + for r.more { + r, simple = parsePath(r.path) + if !simple { + break + } + paths = append(paths, r) + } + } + if !simple { + if del { + return []byte(jstr), + &errorType{"cannot delete value from a complex path"} + } + return setComplexPath(jstr, path, raw, stringify) + } + njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del) + if err != nil { + return []byte(jstr), err + } + return njson, nil +} + +func setComplexPath(jstr, path, raw string, stringify bool) ([]byte, error) { + res := gjson.Get(jstr, path) + if !res.Exists() || !(res.Index != 0 || len(res.Indexes) != 0) { + return []byte(jstr), errNoChange + } + if res.Index != 0 { + njson := []byte(jstr[:res.Index]) + if stringify { + njson = appendStringify(njson, raw) + } else { + njson = append(njson, raw...) + } + njson = append(njson, jstr[res.Index+len(res.Raw):]...) + jstr = string(njson) + } + if len(res.Indexes) > 0 { + type val struct { + index int + res gjson.Result + } + vals := make([]val, 0, len(res.Indexes)) + res.ForEach(func(_, vres gjson.Result) bool { + vals = append(vals, val{res: vres}) + return true + }) + if len(res.Indexes) != len(vals) { + return []byte(jstr), errNoChange + } + for i := 0; i < len(res.Indexes); i++ { + vals[i].index = res.Indexes[i] + } + sort.SliceStable(vals, func(i, j int) bool { + return vals[i].index > vals[j].index + }) + for _, val := range vals { + vres := val.res + index := val.index + njson := []byte(jstr[:index]) + if stringify { + njson = appendStringify(njson, raw) + } else { + njson = append(njson, raw...) + } + njson = append(njson, jstr[index+len(vres.Raw):]...) + jstr = string(njson) + } + } + return []byte(jstr), nil +} + +// SetOptions sets a json value for the specified path with options. +// A path is in dot syntax, such as "name.last" or "age". +// This function expects that the json is well-formed, and does not validate. +// Invalid json will not panic, but it may return back unexpected results. +// An error is returned if the path is not valid. +func SetOptions(json, path string, value interface{}, + opts *Options) (string, error) { + if opts != nil { + if opts.ReplaceInPlace { + // it's not safe to replace bytes in-place for strings + // copy the Options and set options.ReplaceInPlace to false. + nopts := *opts + opts = &nopts + opts.ReplaceInPlace = false + } + } + jsonh := *(*stringHeader)(unsafe.Pointer(&json)) + jsonbh := sliceHeader{data: jsonh.data, len: jsonh.len, cap: jsonh.len} + jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh)) + res, err := SetBytesOptions(jsonb, path, value, opts) + return string(res), err +} + +// SetBytesOptions sets a json value for the specified path with options. +// If working with bytes, this method preferred over +// SetOptions(string(data), path, value) +func SetBytesOptions(json []byte, path string, value interface{}, + opts *Options) ([]byte, error) { + var optimistic, inplace bool + if opts != nil { + optimistic = opts.Optimistic + inplace = opts.ReplaceInPlace + } + jstr := *(*string)(unsafe.Pointer(&json)) + var res []byte + var err error + switch v := value.(type) { + default: + b, merr := jsongo.Marshal(value) + if merr != nil { + return nil, merr + } + raw := *(*string)(unsafe.Pointer(&b)) + res, err = set(jstr, path, raw, false, false, optimistic, inplace) + case dtype: + res, err = set(jstr, path, "", false, true, optimistic, inplace) + case string: + res, err = set(jstr, path, v, true, false, optimistic, inplace) + case []byte: + raw := *(*string)(unsafe.Pointer(&v)) + res, err = set(jstr, path, raw, true, false, optimistic, inplace) + case bool: + if v { + res, err = set(jstr, path, "true", false, false, optimistic, inplace) + } else { + res, err = set(jstr, path, "false", false, false, optimistic, inplace) + } + case int8: + res, err = set(jstr, path, strconv.FormatInt(int64(v), 10), + false, false, optimistic, inplace) + case int16: + res, err = set(jstr, path, strconv.FormatInt(int64(v), 10), + false, false, optimistic, inplace) + case int32: + res, err = set(jstr, path, strconv.FormatInt(int64(v), 10), + false, false, optimistic, inplace) + case int64: + res, err = set(jstr, path, strconv.FormatInt(int64(v), 10), + false, false, optimistic, inplace) + case uint8: + res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10), + false, false, optimistic, inplace) + case uint16: + res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10), + false, false, optimistic, inplace) + case uint32: + res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10), + false, false, optimistic, inplace) + case uint64: + res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10), + false, false, optimistic, inplace) + case float32: + res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64), + false, false, optimistic, inplace) + case float64: + res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64), + false, false, optimistic, inplace) + } + if err == errNoChange { + return json, nil + } + return res, err +} + +// SetRawBytesOptions sets a raw json value for the specified path with options. +// If working with bytes, this method preferred over +// SetRawOptions(string(data), path, value, opts) +func SetRawBytesOptions(json []byte, path string, value []byte, + opts *Options) ([]byte, error) { + jstr := *(*string)(unsafe.Pointer(&json)) + vstr := *(*string)(unsafe.Pointer(&value)) + var optimistic, inplace bool + if opts != nil { + optimistic = opts.Optimistic + inplace = opts.ReplaceInPlace + } + res, err := set(jstr, path, vstr, false, false, optimistic, inplace) + if err == errNoChange { + return json, nil + } + return res, err +} diff --git a/vendor/go.mau.fi/util/LICENSE b/vendor/go.mau.fi/util/LICENSE new file mode 100644 index 00000000..a612ad98 --- /dev/null +++ b/vendor/go.mau.fi/util/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/go.mau.fi/util/base58/README.md b/vendor/go.mau.fi/util/base58/README.md new file mode 100644 index 00000000..50954507 --- /dev/null +++ b/vendor/go.mau.fi/util/base58/README.md @@ -0,0 +1,9 @@ +base58 +========== + +This is a copy of . + +## License + +Package base58 is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/go.mau.fi/util/base58/alphabet.go b/vendor/go.mau.fi/util/base58/alphabet.go new file mode 100644 index 00000000..6bb39fef --- /dev/null +++ b/vendor/go.mau.fi/util/base58/alphabet.go @@ -0,0 +1,49 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// AUTOGENERATED by genalphabet.go; do not edit. + +package base58 + +const ( + // alphabet is the modified base58 alphabet used by Bitcoin. + alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + alphabetIdx0 = '1' +) + +var b58 = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 255, 255, 255, 255, 255, 255, + 255, 9, 10, 11, 12, 13, 14, 15, + 16, 255, 17, 18, 19, 20, 21, 255, + 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 255, 255, 255, 255, 255, + 255, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 255, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, +} diff --git a/vendor/go.mau.fi/util/base58/base58.go b/vendor/go.mau.fi/util/base58/base58.go new file mode 100644 index 00000000..bd0ea47c --- /dev/null +++ b/vendor/go.mau.fi/util/base58/base58.go @@ -0,0 +1,142 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58 + +import ( + "math/big" +) + +//go:generate go run genalphabet.go + +var bigRadix = [...]*big.Int{ + big.NewInt(0), + big.NewInt(58), + big.NewInt(58 * 58), + big.NewInt(58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), + bigRadix10, +} + +var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10 + +// Decode decodes a modified base58 string to a byte slice. +func Decode(b string) []byte { + answer := big.NewInt(0) + scratch := new(big.Int) + + // Calculating with big.Int is slow for each iteration. + // x += b58[b[i]] * j + // j *= 58 + // + // Instead we can try to do as much calculations on int64. + // We can represent a 10 digit base58 number using an int64. + // + // Hence we'll try to convert 10, base58 digits at a time. + // The rough idea is to calculate `t`, such that: + // + // t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0 + // x *= 58^10 + // x += t + // + // Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10. + // In that case we'll use the bigRadix[n] lookup for the appropriate power. + for t := b; len(t) > 0; { + n := len(t) + if n > 10 { + n = 10 + } + + total := uint64(0) + for _, v := range t[:n] { + if v > 255 { + return []byte("") + } + + tmp := b58[v] + if tmp == 255 { + return []byte("") + } + total = total*58 + uint64(tmp) + } + + answer.Mul(answer, bigRadix[n]) + scratch.SetUint64(total) + answer.Add(answer, scratch) + + t = t[n:] + } + + tmpval := answer.Bytes() + + var numZeros int + for numZeros = 0; numZeros < len(b); numZeros++ { + if b[numZeros] != alphabetIdx0 { + break + } + } + flen := numZeros + len(tmpval) + val := make([]byte, flen) + copy(val[numZeros:], tmpval) + + return val +} + +// Encode encodes a byte slice to a modified base58 string. +func Encode(b []byte) string { + x := new(big.Int) + x.SetBytes(b) + + // maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58) + maxlen := int(float64(len(b))*1.365658237309761) + 1 + answer := make([]byte, 0, maxlen) + mod := new(big.Int) + for x.Sign() > 0 { + // Calculating with big.Int is slow for each iteration. + // x, mod = x / 58, x % 58 + // + // Instead we can try to do as much calculations on int64. + // x, mod = x / 58^10, x % 58^10 + // + // Which will give us mod, which is 10 digit base58 number. + // We'll loop that 10 times to convert to the answer. + + x.DivMod(x, bigRadix10, mod) + if x.Sign() == 0 { + // When x = 0, we need to ensure we don't add any extra zeros. + m := mod.Int64() + for m > 0 { + answer = append(answer, alphabet[m%58]) + m /= 58 + } + } else { + m := mod.Int64() + for i := 0; i < 10; i++ { + answer = append(answer, alphabet[m%58]) + m /= 58 + } + } + } + + // leading zero bytes + for _, i := range b { + if i != 0 { + break + } + answer = append(answer, alphabetIdx0) + } + + // reverse + alen := len(answer) + for i := 0; i < alen/2; i++ { + answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] + } + + return string(answer) +} diff --git a/vendor/go.mau.fi/util/base58/base58check.go b/vendor/go.mau.fi/util/base58/base58check.go new file mode 100644 index 00000000..402c3233 --- /dev/null +++ b/vendor/go.mau.fi/util/base58/base58check.go @@ -0,0 +1,52 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58 + +import ( + "crypto/sha256" + "errors" +) + +// ErrChecksum indicates that the checksum of a check-encoded string does not verify against +// the checksum. +var ErrChecksum = errors.New("checksum error") + +// ErrInvalidFormat indicates that the check-encoded string has an invalid format. +var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing") + +// checksum: first four bytes of sha256^2 +func checksum(input []byte) (cksum [4]byte) { + h := sha256.Sum256(input) + h2 := sha256.Sum256(h[:]) + copy(cksum[:], h2[:4]) + return +} + +// CheckEncode prepends a version byte and appends a four byte checksum. +func CheckEncode(input []byte, version byte) string { + b := make([]byte, 0, 1+len(input)+4) + b = append(b, version) + b = append(b, input...) + cksum := checksum(b) + b = append(b, cksum[:]...) + return Encode(b) +} + +// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum. +func CheckDecode(input string) (result []byte, version byte, err error) { + decoded := Decode(input) + if len(decoded) < 5 { + return nil, 0, ErrInvalidFormat + } + version = decoded[0] + var cksum [4]byte + copy(cksum[:], decoded[len(decoded)-4:]) + if checksum(decoded[:len(decoded)-4]) != cksum { + return nil, 0, ErrChecksum + } + payload := decoded[1 : len(decoded)-4] + result = append(result, payload...) + return +} diff --git a/vendor/go.mau.fi/util/base58/doc.go b/vendor/go.mau.fi/util/base58/doc.go new file mode 100644 index 00000000..d657f050 --- /dev/null +++ b/vendor/go.mau.fi/util/base58/doc.go @@ -0,0 +1,29 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package base58 provides an API for working with modified base58 and Base58Check +encodings. + +# Modified Base58 Encoding + +Standard base58 encoding is similar to standard base64 encoding except, as the +name implies, it uses a 58 character alphabet which results in an alphanumeric +string and allows some characters which are problematic for humans to be +excluded. Due to this, there can be various base58 alphabets. + +The modified base58 alphabet used by Bitcoin, and hence this package, omits the +0, O, I, and l characters that look the same in many fonts and are therefore +hard to humans to distinguish. + +# Base58Check Encoding Scheme + +The Base58Check encoding scheme is primarily used for Bitcoin addresses at the +time of this writing, however it can be used to generically encode arbitrary +byte arrays into human-readable strings along with a version byte that can be +used to differentiate the same payload. For Bitcoin addresses, the extra +version is used to differentiate the network of otherwise identical public keys +which helps prevent using an address intended for one network on another. +*/ +package base58 diff --git a/vendor/go.mau.fi/util/exbytes/string.go b/vendor/go.mau.fi/util/exbytes/string.go new file mode 100644 index 00000000..0888698a --- /dev/null +++ b/vendor/go.mau.fi/util/exbytes/string.go @@ -0,0 +1,20 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exbytes + +import ( + "unsafe" +) + +// UnsafeString returns a string that points to the same memory as the input byte slice. +// +// The input byte slice must not be modified after this function is called. +// +// See [go.mau.fi/util/exstrings.UnsafeBytes] for the reverse operation. +func UnsafeString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} diff --git a/vendor/go.mau.fi/util/exbytes/writer.go b/vendor/go.mau.fi/util/exbytes/writer.go new file mode 100644 index 00000000..1a45837f --- /dev/null +++ b/vendor/go.mau.fi/util/exbytes/writer.go @@ -0,0 +1,78 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exbytes + +import ( + "errors" + "fmt" + "io" +) + +// Writer is a simple byte writer that does not allow extending the buffer. +// +// Writes always go after the current len() and will fail if the slice capacity is too low. +// +// The correct way to use this is to create a slice with sufficient capacity and zero length: +// +// x := make([]byte, 0, 11) +// w := (*Writer)(&x) +// w.Write([]byte("hello")) +// w.WriteByte(' ') +// w.WriteString("world") +// fmt.Println(string(x)) // "hello world" +type Writer []byte + +var ErrWriterBufferFull = errors.New("exbytes.Writer: buffer full") + +var ( + _ io.Writer = (*Writer)(nil) + _ io.StringWriter = (*Writer)(nil) + _ io.ByteWriter = (*Writer)(nil) +) + +func (w *Writer) extendLen(n int) (int, error) { + ptr := len(*w) + if ptr+n > cap(*w) { + return 0, fmt.Errorf("%w (%d + %d > %d)", ErrWriterBufferFull, ptr, n, cap(*w)) + } + *w = (*w)[:ptr+n] + return ptr, nil +} + +func (w *Writer) Write(b []byte) (n int, err error) { + ptr, err := w.extendLen(len(b)) + if err != nil { + return 0, err + } + copy((*w)[ptr:], b) + return len(b), nil +} + +func (w *Writer) WriteString(s string) (n int, err error) { + ptr, err := w.extendLen(len(s)) + if err != nil { + return 0, err + } + copy((*w)[ptr:], s) + return len(s), nil +} + +func (w *Writer) WriteByte(r byte) error { + ptr, err := w.extendLen(1) + if err != nil { + return err + } + (*w)[ptr] = r + return nil +} + +func (w *Writer) String() string { + if w == nil { + return "" + } + return string(*w) +} diff --git a/vendor/go.mau.fi/util/exerrors/dualerror.go b/vendor/go.mau.fi/util/exerrors/dualerror.go new file mode 100644 index 00000000..79acd065 --- /dev/null +++ b/vendor/go.mau.fi/util/exerrors/dualerror.go @@ -0,0 +1,33 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exerrors + +import ( + "errors" + "fmt" +) + +type DualError struct { + High error + Low error +} + +func NewDualError(high, low error) DualError { + return DualError{high, low} +} + +func (err DualError) Is(other error) bool { + return errors.Is(other, err.High) || errors.Is(other, err.Low) +} + +func (err DualError) Unwrap() error { + return err.Low +} + +func (err DualError) Error() string { + return fmt.Sprintf("%v: %v", err.High, err.Low) +} diff --git a/vendor/go.mau.fi/util/exerrors/must.go b/vendor/go.mau.fi/util/exerrors/must.go new file mode 100644 index 00000000..2ffda303 --- /dev/null +++ b/vendor/go.mau.fi/util/exerrors/must.go @@ -0,0 +1,23 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exerrors + +func Must[T any](val T, err error) T { + PanicIfNotNil(err) + return val +} + +func Must2[T any, T2 any](val T, val2 T2, err error) (T, T2) { + PanicIfNotNil(err) + return val, val2 +} + +func PanicIfNotNil(err error) { + if err != nil { + panic(err) + } +} diff --git a/vendor/go.mau.fi/util/exgjson/gjson.go b/vendor/go.mau.fi/util/exgjson/gjson.go new file mode 100644 index 00000000..ee8fcdd8 --- /dev/null +++ b/vendor/go.mau.fi/util/exgjson/gjson.go @@ -0,0 +1,33 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exgjson + +import ( + "strings" +) + +// Escaper escapes a string for use in a GJSON path. +var Escaper = strings.NewReplacer( + `\`, `\\`, + ".", `\.`, + "|", `\|`, + "#", `\#`, + "@", `\@`, + "*", `\*`, + "?", `\?`) + +// Path returns a GJSON path pointing at a nested object, with each provided string being a key. +func Path(path ...string) string { + var result strings.Builder + for i, part := range path { + _, _ = Escaper.WriteString(&result, part) + if i < len(path)-1 { + result.WriteRune('.') + } + } + return result.String() +} diff --git a/vendor/go.mau.fi/util/exhttp/cors.go b/vendor/go.mau.fi/util/exhttp/cors.go new file mode 100644 index 00000000..a27bda22 --- /dev/null +++ b/vendor/go.mau.fi/util/exhttp/cors.go @@ -0,0 +1,32 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exhttp + +import "net/http" + +func AddCORSHeaders(w http.ResponseWriter) { + // Recommended CORS headers can be found in https://spec.matrix.org/v1.3/client-server-api/#web-browser-clients + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization") + w.Header().Set("Content-Security-Policy", "sandbox; default-src 'none'; script-src 'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';") + // Allow browsers to cache above for 1 day + w.Header().Set("Access-Control-Max-Age", "86400") +} + +// CORSMiddleware adds CORS headers to the response and handles OPTIONS +// requests by returning 200 OK immediately. +func CORSMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + AddCORSHeaders(w) + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + next.ServeHTTP(w, r) + }) +} diff --git a/vendor/go.mau.fi/util/exhttp/handleerrors.go b/vendor/go.mau.fi/util/exhttp/handleerrors.go new file mode 100644 index 00000000..03413d29 --- /dev/null +++ b/vendor/go.mau.fi/util/exhttp/handleerrors.go @@ -0,0 +1,98 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exhttp + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "net/http" +) + +type ErrorBodies struct { + NotFound json.RawMessage + MethodNotAllowed json.RawMessage +} + +func HandleErrors(gen ErrorBodies) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + next.ServeHTTP(&bodyOverrider{ + ResponseWriter: w, + statusNotFoundBody: gen.NotFound, + statusMethodNotAllowedBody: gen.MethodNotAllowed, + }, r) + }) + } +} + +type bodyOverrider struct { + http.ResponseWriter + + code int + override bool + written bool + + hijacked bool + + statusNotFoundBody json.RawMessage + statusMethodNotAllowedBody json.RawMessage +} + +var ( + _ http.ResponseWriter = (*bodyOverrider)(nil) + _ http.Flusher = (*bodyOverrider)(nil) + _ http.Hijacker = (*bodyOverrider)(nil) +) + +func (b *bodyOverrider) WriteHeader(code int) { + if !b.hijacked && + b.Header().Get("Content-Type") == "text/plain; charset=utf-8" && + (code == http.StatusNotFound || code == http.StatusMethodNotAllowed) { + + b.Header().Set("Content-Type", "application/json") + b.override = true + } + + b.code = code + b.ResponseWriter.WriteHeader(code) +} + +func (b *bodyOverrider) Write(body []byte) (n int, err error) { + if b.override { + n = len(body) + if !b.written { + switch b.code { + case http.StatusNotFound: + _, err = b.ResponseWriter.Write(b.statusNotFoundBody) + case http.StatusMethodNotAllowed: + _, err = b.ResponseWriter.Write(b.statusMethodNotAllowedBody) + } + } + b.written = true + return + } + + return b.ResponseWriter.Write(body) +} + +func (b *bodyOverrider) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := b.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("HandleErrors: %T does not implement http.Hijacker", b.ResponseWriter) + } + b.hijacked = true + return hijacker.Hijack() +} + +func (b *bodyOverrider) Flush() { + flusher, ok := b.ResponseWriter.(http.Flusher) + if ok { + flusher.Flush() + } +} diff --git a/vendor/go.mau.fi/util/exhttp/json.go b/vendor/go.mau.fi/util/exhttp/json.go new file mode 100644 index 00000000..91d7f0e7 --- /dev/null +++ b/vendor/go.mau.fi/util/exhttp/json.go @@ -0,0 +1,36 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exhttp + +import ( + "encoding/json" + "net/http" +) + +var AutoAllowCORS = true + +func WriteJSONResponse(w http.ResponseWriter, httpStatusCode int, jsonData any) { + if AutoAllowCORS { + AddCORSHeaders(w) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(httpStatusCode) + _ = json.NewEncoder(w).Encode(jsonData) +} + +func WriteJSONData(w http.ResponseWriter, httpStatusCode int, data []byte) { + if AutoAllowCORS { + AddCORSHeaders(w) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(httpStatusCode) + _, _ = w.Write(data) +} + +func WriteEmptyJSONResponse(w http.ResponseWriter, httpStatusCode int) { + WriteJSONData(w, httpStatusCode, []byte("{}")) +} diff --git a/vendor/go.mau.fi/util/exhttp/middleware.go b/vendor/go.mau.fi/util/exhttp/middleware.go new file mode 100644 index 00000000..fb1ab592 --- /dev/null +++ b/vendor/go.mau.fi/util/exhttp/middleware.go @@ -0,0 +1,30 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exhttp + +import "net/http" + +// Middleware represents a middleware that can be applied to an [http.Handler]. +type Middleware func(http.Handler) http.Handler + +// ApplyMiddleware applies the provided [Middleware] functions to the given +// router. The middlewares will be applied in the order they are provided. +func ApplyMiddleware(router http.Handler, middlewares ...Middleware) http.Handler { + // Apply middlewares in reverse order because the first middleware provided + // needs to be the outermost one. + for i := len(middlewares) - 1; i >= 0; i-- { + router = middlewares[i](router) + } + return router +} + +// StripPrefix is a wrapper for [http.StripPrefix] is compatible with the middleware pattern. +func StripPrefix(prefix string) Middleware { + return func(next http.Handler) http.Handler { + return http.StripPrefix(prefix, next) + } +} diff --git a/vendor/go.mau.fi/util/exhttp/networkerror.go b/vendor/go.mau.fi/util/exhttp/networkerror.go new file mode 100644 index 00000000..fc3d098a --- /dev/null +++ b/vendor/go.mau.fi/util/exhttp/networkerror.go @@ -0,0 +1,38 @@ +// Copyright (c) 2025 Toni Spets +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exhttp + +import ( + "errors" + "net" + "syscall" + + "golang.org/x/net/http2" +) + +func IsNetworkError(err error) bool { + if errno := syscall.Errno(0); errors.As(err, &errno) { + // common errnos for network related operations + return errno == syscall.ENETDOWN || + errno == syscall.ENETUNREACH || + errno == syscall.ENETRESET || + errno == syscall.ECONNABORTED || + errno == syscall.ECONNRESET || + errno == syscall.ENOBUFS || + errno == syscall.ETIMEDOUT || + errno == syscall.ECONNREFUSED || + errno == syscall.EHOSTDOWN || + errno == syscall.EHOSTUNREACH || + errno == syscall.EPIPE + } else if netError := net.Error(nil); errors.As(err, &netError) { + return true + } else if errors.As(err, &http2.StreamError{}) { + return true + } + + return false +} diff --git a/vendor/go.mau.fi/util/exmaps/clone.go b/vendor/go.mau.fi/util/exmaps/clone.go new file mode 100644 index 00000000..4ae98815 --- /dev/null +++ b/vendor/go.mau.fi/util/exmaps/clone.go @@ -0,0 +1,19 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package exmaps + +import ( + "maps" +) + +// NonNilClone returns a copy of the given map, or an empty map if the input is nil. +func NonNilClone[Key comparable, Value any](m map[Key]Value) map[Key]Value { + if m == nil { + return make(map[Key]Value) + } + return maps.Clone(m) +} diff --git a/vendor/go.mau.fi/util/glob/glob.go b/vendor/go.mau.fi/util/glob/glob.go new file mode 100644 index 00000000..67fce3e9 --- /dev/null +++ b/vendor/go.mau.fi/util/glob/glob.go @@ -0,0 +1,100 @@ +// Package glob implements very simple glob pattern matching used in various parts of the Matrix spec, +// such as push rules and moderation policy lists. +// +// See https://spec.matrix.org/v1.11/appendices/#glob-style-matching for more info. +package glob + +import ( + "strings" +) + +type Glob interface { + Match(string) bool +} + +var ( + _ Glob = ExactGlob("") + _ Glob = PrefixGlob("") + _ Glob = SuffixGlob("") + _ Glob = ContainsGlob("") + _ Glob = (*PrefixAndSuffixGlob)(nil) + _ Glob = (*PrefixSuffixAndContainsGlob)(nil) + _ Glob = (*RegexGlob)(nil) +) + +// Compile compiles a glob pattern into an object that can be used to efficiently match strings against the pattern. +// +// Simple globs will be converted into prefix/suffix/contains checks, while complicated ones will be compiled as regex. +func Compile(pattern string) Glob { + pattern = Simplify(pattern) + g := compileSimple(pattern) + if g != nil { + return g + } + g, _ = CompileRegex(pattern) + return g +} + +// CompileWithImplicitContains is a wrapper for Compile which will replace exact matches with contains matches. +// i.e. if the pattern has no wildcards, it will be treated as if it was surrounded in asterisks (`foo` -> `*foo*`). +func CompileWithImplicitContains(pattern string) Glob { + g := Compile(pattern) + if _, isExact := g.(ExactGlob); isExact { + return ContainsGlob(pattern) + } + return g +} + +// CompileSimple compiles a glob pattern into one of the non-regex forms. +// +// If the pattern can't be compiled into a simple form, it returns nil. +func CompileSimple(pattern string) Glob { + return compileSimple(Simplify(pattern)) +} + +func compileSimple(pattern string) Glob { + if strings.ContainsRune(pattern, '?') { + return nil + } + switch strings.Count(pattern, "*") { + case 0: + return ExactGlob(pattern) + case 1: + if strings.HasPrefix(pattern, "*") { + return SuffixGlob(pattern[1:]) + } else if strings.HasSuffix(pattern, "*") { + return PrefixGlob(pattern[:len(pattern)-1]) + } else { + parts := strings.Split(pattern, "*") + return PrefixAndSuffixGlob{ + Prefix: parts[0], + Suffix: parts[1], + } + } + case 2: + if strings.HasPrefix(pattern, "*") && strings.HasSuffix(pattern, "*") { + return ContainsGlob(pattern[1 : len(pattern)-1]) + } + parts := strings.Split(pattern, "*") + return PrefixSuffixAndContainsGlob{ + Prefix: parts[0], + Contains: parts[1], + Suffix: parts[2], + } + default: + return nil + } +} + +var sqlCompiler = strings.NewReplacer( + `\`, `\\`, + `%`, `\%`, + `_`, `\_`, + `*`, `%`, + `?`, `_`, +) + +// ToSQL converts a Matrix glob pattern to a SQL LIKE pattern. +func ToSQL(pattern string) string { + return sqlCompiler.Replace(Simplify(pattern)) +} diff --git a/vendor/go.mau.fi/util/glob/regex.go b/vendor/go.mau.fi/util/glob/regex.go new file mode 100644 index 00000000..4d799d81 --- /dev/null +++ b/vendor/go.mau.fi/util/glob/regex.go @@ -0,0 +1,77 @@ +package glob + +import ( + "fmt" + "io" + "regexp" + "strings" +) + +type RegexGlob struct { + regex *regexp.Regexp +} + +func (rg *RegexGlob) Match(s string) bool { + return rg.regex.MatchString(s) +} + +type swWriter interface { + io.StringWriter + io.Writer +} + +// ToRegexPattern converts a glob pattern to a regex pattern and writes it to the given buffer. +// +// Only errors returned by the Write calls are returned by this function +// (so if a non-erroring writer is used, this function will always return nil). +func ToRegexPattern(pattern string, buf swWriter) error { + for _, part := range SplitPattern(pattern) { + if strings.ContainsRune(part, '*') || strings.ContainsRune(part, '?') { + questions := strings.Count(part, "?") + star := strings.ContainsRune(part, '*') + if star { + if questions > 0 { + _, err := fmt.Fprintf(buf, ".{%d,}", questions) + if err != nil { + return err + } + } else { + _, err := buf.WriteString(".*") + if err != nil { + return err + } + } + } else if questions > 0 { + _, err := fmt.Fprintf(buf, ".{%d}", questions) + if err != nil { + return err + } + } + } else { + _, err := buf.WriteString(regexp.QuoteMeta(part)) + if err != nil { + return err + } + } + } + return nil +} + +// CompileRegex compiles the given glob pattern into a regex pattern. +// +// If you want the raw regex string, use [ToRegexPattern] instead +func CompileRegex(pattern string) (*RegexGlob, error) { + var buf strings.Builder + buf.WriteRune('^') + err := ToRegexPattern(pattern, &buf) + if err != nil { + // This will never actually happen + return nil, err + } + buf.WriteRune('$') + regex, err := regexp.Compile(buf.String()) + if err != nil { + return nil, err + } + return &RegexGlob{regex}, nil +} diff --git a/vendor/go.mau.fi/util/glob/simple.go b/vendor/go.mau.fi/util/glob/simple.go new file mode 100644 index 00000000..bb9bf6d5 --- /dev/null +++ b/vendor/go.mau.fi/util/glob/simple.go @@ -0,0 +1,62 @@ +package glob + +import ( + "strings" +) + +// ExactGlob is the result of [Compile] when the pattern contains no glob characters. +// It uses a simple string comparison to match. +type ExactGlob string + +func (eg ExactGlob) Match(s string) bool { + return string(eg) == s +} + +// SuffixGlob is the result of [Compile] when the pattern only has one `*` at the beginning. +// It uses [strings.HasSuffix] to match. +type SuffixGlob string + +func (sg SuffixGlob) Match(s string) bool { + return strings.HasSuffix(s, string(sg)) +} + +// PrefixGlob is the result of [Compile] when the pattern only has one `*` at the end. +// It uses [strings.HasPrefix] to match. +type PrefixGlob string + +func (pg PrefixGlob) Match(s string) bool { + return strings.HasPrefix(s, string(pg)) +} + +// ContainsGlob is the result of [Compile] when the pattern has two `*`s, one at the beginning and one at the end. +// It uses [strings.Contains] to match. +// +// When there are exactly two `*`s, but they're not surrounding the string, the pattern is compiled as a [PrefixSuffixAndContainsGlob] instead. +type ContainsGlob string + +func (cg ContainsGlob) Match(s string) bool { + return strings.Contains(s, string(cg)) +} + +// PrefixAndSuffixGlob is the result of [Compile] when the pattern only has one `*` in the middle. +type PrefixAndSuffixGlob struct { + Prefix string + Suffix string +} + +func (psg PrefixAndSuffixGlob) Match(s string) bool { + return strings.HasPrefix(s, psg.Prefix) && strings.HasSuffix(s[len(psg.Prefix):], psg.Suffix) +} + +// PrefixSuffixAndContainsGlob is the result of [Compile] when the pattern has two `*`s which are not surrounding the rest of the pattern. +type PrefixSuffixAndContainsGlob struct { + Prefix string + Suffix string + Contains string +} + +func (psacg PrefixSuffixAndContainsGlob) Match(s string) bool { + return strings.HasPrefix(s, psacg.Prefix) && + strings.HasSuffix(s[len(psacg.Prefix):], psacg.Suffix) && + strings.Contains(s[len(psacg.Prefix):len(s)-len(psacg.Suffix)], psacg.Contains) +} diff --git a/vendor/go.mau.fi/util/glob/util.go b/vendor/go.mau.fi/util/glob/util.go new file mode 100644 index 00000000..37d9b2d0 --- /dev/null +++ b/vendor/go.mau.fi/util/glob/util.go @@ -0,0 +1,42 @@ +package glob + +import ( + "regexp" + "strings" +) + +var redundantStarRegex = regexp.MustCompile(`\*{2,}`) +var maybeRedundantQuestionRegex = regexp.MustCompile(`[*?]{2,}`) +var wildcardRegex = regexp.MustCompile(`[*?]+`) + +func SplitPattern(pattern string) []string { + indexes := wildcardRegex.FindAllStringIndex(pattern, -1) + if len(indexes) == 0 { + return []string{pattern} + } + parts := make([]string, 0, len(indexes)+1) + start := 0 + for _, part := range indexes { + end := part[0] + if end > start { + parts = append(parts, pattern[start:end]) + } + parts = append(parts, pattern[part[0]:part[1]]) + start = part[1] + } + if start < len(pattern) { + parts = append(parts, pattern[start:]) + } + return parts +} + +func Simplify(pattern string) string { + pattern = redundantStarRegex.ReplaceAllString(pattern, "*") + pattern = maybeRedundantQuestionRegex.ReplaceAllStringFunc(pattern, func(s string) string { + if !strings.ContainsRune(s, '*') { + return s + } + return strings.Repeat("?", strings.Count(s, "?")) + "*" + }) + return pattern +} diff --git a/vendor/go.mau.fi/util/jsonbytes/unpadded.go b/vendor/go.mau.fi/util/jsonbytes/unpadded.go new file mode 100644 index 00000000..d7d17dea --- /dev/null +++ b/vendor/go.mau.fi/util/jsonbytes/unpadded.go @@ -0,0 +1,48 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package jsonbytes + +import ( + "encoding/base64" + "encoding/json" +) + +// UnpaddedBytes is a byte slice that is encoded and decoded using +// [base64.RawStdEncoding] instead of the default padded base64. +type UnpaddedBytes []byte + +func (b UnpaddedBytes) MarshalJSON() ([]byte, error) { + return json.Marshal(base64.RawStdEncoding.EncodeToString(b)) +} + +func (b *UnpaddedBytes) UnmarshalJSON(data []byte) error { + var b64str string + err := json.Unmarshal(data, &b64str) + if err != nil { + return err + } + *b, err = base64.RawStdEncoding.DecodeString(b64str) + return err +} + +// UnpaddedURLBytes is a byte slice that is encoded and decoded using +// [base64.RawURLEncoding] instead of the default padded base64. +type UnpaddedURLBytes []byte + +func (b UnpaddedURLBytes) MarshalJSON() ([]byte, error) { + return json.Marshal(base64.RawURLEncoding.EncodeToString(b)) +} + +func (b *UnpaddedURLBytes) UnmarshalJSON(data []byte) error { + var b64str string + err := json.Unmarshal(data, &b64str) + if err != nil { + return err + } + *b, err = base64.RawURLEncoding.DecodeString(b64str) + return err +} diff --git a/vendor/go.mau.fi/util/jsontime/duration.go b/vendor/go.mau.fi/util/jsontime/duration.go new file mode 100644 index 00000000..bfa9b668 --- /dev/null +++ b/vendor/go.mau.fi/util/jsontime/duration.go @@ -0,0 +1,155 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package jsontime + +import ( + "database/sql/driver" + "strconv" + "time" +) + +func unmarshalDuration(into *time.Duration, jsonData []byte, unit time.Duration) error { + val, err := strconv.ParseInt(string(jsonData), 10, 64) + if err != nil { + return err + } + *into = time.Duration(val) * unit + return nil +} + +func anyIntegerToDuration(src any, unit time.Duration, into *time.Duration) error { + i64, err := anyIntegerTo64(src) + if err != nil { + return err + } + *into = time.Duration(i64) * unit + return nil +} + +type Seconds struct { + time.Duration +} + +func S(dur time.Duration) Seconds { + return Seconds{Duration: dur} +} + +func SInt(dur int) Seconds { + return Seconds{Duration: time.Duration(dur) * time.Second} +} + +func (s Seconds) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(int64(s.Seconds()), 10)), nil +} + +func (s Seconds) Value() (driver.Value, error) { + return int64(s.Seconds()), nil +} + +func (s *Seconds) UnmarshalJSON(data []byte) error { + return unmarshalDuration(&s.Duration, data, time.Second) +} + +func (s *Seconds) Scan(src interface{}) error { + return anyIntegerToDuration(src, time.Second, &s.Duration) +} + +func (s *Seconds) Get() time.Duration { + if s == nil { + return 0 + } + return s.Duration +} + +type Milliseconds struct { + time.Duration +} + +func MS(dur time.Duration) Milliseconds { + return Milliseconds{Duration: dur} +} + +func MSInt(dur int64) Milliseconds { + return Milliseconds{Duration: time.Duration(dur) * time.Millisecond} +} + +func (s Milliseconds) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(s.Milliseconds(), 10)), nil +} + +func (s Milliseconds) Value() (driver.Value, error) { + return s.Milliseconds(), nil +} + +func (s *Milliseconds) UnmarshalJSON(data []byte) error { + return unmarshalDuration(&s.Duration, data, time.Millisecond) +} + +func (s *Milliseconds) Scan(src interface{}) error { + return anyIntegerToDuration(src, time.Millisecond, &s.Duration) +} + +func (s *Milliseconds) Get() time.Duration { + if s == nil { + return 0 + } + return s.Duration +} + +type Microseconds struct { + time.Duration +} + +func (s Microseconds) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(s.Microseconds(), 10)), nil +} + +func (s Microseconds) Value() (driver.Value, error) { + return s.Microseconds(), nil +} + +func (s *Microseconds) UnmarshalJSON(data []byte) error { + return unmarshalDuration(&s.Duration, data, time.Microsecond) +} + +func (s *Microseconds) Scan(src interface{}) error { + return anyIntegerToDuration(src, time.Microsecond, &s.Duration) +} + +func (s *Microseconds) Get() time.Duration { + if s == nil { + return 0 + } + return s.Duration +} + +type Nanoseconds struct { + time.Duration +} + +func (s Nanoseconds) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(s.Nanoseconds(), 10)), nil +} + +func (s Nanoseconds) Value() (driver.Value, error) { + return s.Nanoseconds(), nil +} + +func (s *Nanoseconds) UnmarshalJSON(data []byte) error { + return unmarshalDuration(&s.Duration, data, time.Nanosecond) +} + +func (s *Nanoseconds) Scan(src interface{}) error { + return anyIntegerToDuration(src, time.Nanosecond, &s.Duration) +} + +func (s *Nanoseconds) Get() time.Duration { + if s == nil { + return 0 + } + return s.Duration +} diff --git a/vendor/go.mau.fi/util/jsontime/helpers.go b/vendor/go.mau.fi/util/jsontime/helpers.go new file mode 100644 index 00000000..b90461c6 --- /dev/null +++ b/vendor/go.mau.fi/util/jsontime/helpers.go @@ -0,0 +1,70 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package jsontime + +import ( + "time" +) + +func zeroSafeUnixToTime(val int64, fn func(int64) time.Time) time.Time { + if val == 0 { + return time.Time{} + } + return fn(val) +} + +func UM(time time.Time) UnixMilli { + return UnixMilli{Time: time} +} + +func UMInt(ts int64) UnixMilli { + return UM(zeroSafeUnixToTime(ts, time.UnixMilli)) +} + +func UnixMilliNow() UnixMilli { + return UM(time.Now()) +} + +func UMicro(time time.Time) UnixMicro { + return UnixMicro{Time: time} +} + +func UMicroInt(ts int64) UnixMicro { + return UMicro(zeroSafeUnixToTime(ts, time.UnixMicro)) +} + +func UnixMicroNow() UnixMicro { + return UMicro(time.Now()) +} + +func UN(time time.Time) UnixNano { + return UnixNano{Time: time} +} + +func UNInt(ts int64) UnixNano { + return UN(zeroSafeUnixToTime(ts, func(i int64) time.Time { + return time.Unix(0, i) + })) +} + +func UnixNanoNow() UnixNano { + return UN(time.Now()) +} + +func U(time time.Time) Unix { + return Unix{Time: time} +} + +func UInt(ts int64) Unix { + return U(zeroSafeUnixToTime(ts, func(i int64) time.Time { + return time.Unix(i, 0) + })) +} + +func UnixNow() Unix { + return U(time.Now()) +} diff --git a/vendor/go.mau.fi/util/jsontime/integer.go b/vendor/go.mau.fi/util/jsontime/integer.go new file mode 100644 index 00000000..56a4f2a5 --- /dev/null +++ b/vendor/go.mau.fi/util/jsontime/integer.go @@ -0,0 +1,185 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package jsontime + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "time" +) + +var ErrNotInteger = errors.New("value is not an integer") + +func parseTime(data []byte, unixConv func(int64) time.Time, into *time.Time) error { + var val int64 + err := json.Unmarshal(data, &val) + if err != nil { + return err + } + if val == 0 { + *into = time.Time{} + } else { + *into = unixConv(val) + } + return nil +} + +func anyIntegerTo64(src any) (int64, error) { + switch v := src.(type) { + case int: + return int64(v), nil + case int8: + return int64(v), nil + case int16: + return int64(v), nil + case int32: + return int64(v), nil + case int64: + return v, nil + default: + return 0, fmt.Errorf("%w: %T", ErrNotInteger, src) + } +} + +func anyIntegerToTime(src any, unixConv func(int64) time.Time, into *time.Time) error { + i64, err := anyIntegerTo64(src) + if err != nil { + return err + } + *into = unixConv(i64) + return nil +} + +func zeroSafeUnix(t time.Time, method func(time.Time) int64) int64 { + if t.IsZero() { + return 0 + } + return method(t) +} + +var _ sql.Scanner = &UnixMilli{} +var _ driver.Valuer = UnixMilli{} + +type UnixMilli struct { + time.Time +} + +func (um UnixMilli) MarshalJSON() ([]byte, error) { + return json.Marshal(um.UnixMilli()) +} + +func (um *UnixMilli) UnmarshalJSON(data []byte) error { + return parseTime(data, time.UnixMilli, &um.Time) +} + +func (um UnixMilli) Value() (driver.Value, error) { + return um.UnixMilli(), nil +} + +func (um *UnixMilli) Scan(src any) error { + return anyIntegerToTime(src, time.UnixMilli, &um.Time) +} + +func (um UnixMilli) Unix() int64 { return zeroSafeUnix(um.Time, time.Time.Unix) } +func (um UnixMilli) UnixMilli() int64 { return zeroSafeUnix(um.Time, time.Time.UnixMilli) } +func (um UnixMilli) UnixMicro() int64 { return zeroSafeUnix(um.Time, time.Time.UnixMicro) } +func (um UnixMilli) UnixNano() int64 { return zeroSafeUnix(um.Time, time.Time.UnixNano) } + +var _ sql.Scanner = &UnixMicro{} +var _ driver.Valuer = UnixMicro{} + +type UnixMicro struct { + time.Time +} + +func (um UnixMicro) MarshalJSON() ([]byte, error) { + return json.Marshal(um.UnixMicro()) +} + +func (um *UnixMicro) UnmarshalJSON(data []byte) error { + return parseTime(data, time.UnixMicro, &um.Time) +} + +func (um UnixMicro) Value() (driver.Value, error) { + return um.UnixMicro(), nil +} + +func (um *UnixMicro) Scan(src any) error { + return anyIntegerToTime(src, time.UnixMicro, &um.Time) +} + +func (um UnixMicro) Unix() int64 { return zeroSafeUnix(um.Time, time.Time.Unix) } +func (um UnixMicro) UnixMilli() int64 { return zeroSafeUnix(um.Time, time.Time.UnixMilli) } +func (um UnixMicro) UnixMicro() int64 { return zeroSafeUnix(um.Time, time.Time.UnixMicro) } +func (um UnixMicro) UnixNano() int64 { return zeroSafeUnix(um.Time, time.Time.UnixNano) } + +var _ sql.Scanner = &UnixNano{} +var _ driver.Valuer = UnixNano{} + +type UnixNano struct { + time.Time +} + +func (un UnixNano) MarshalJSON() ([]byte, error) { + return json.Marshal(un.UnixNano()) +} + +func (un *UnixNano) UnmarshalJSON(data []byte) error { + return parseTime(data, func(i int64) time.Time { + return time.Unix(0, i) + }, &un.Time) +} + +func (un UnixNano) Value() (driver.Value, error) { + return un.UnixNano(), nil +} + +func (un *UnixNano) Scan(src any) error { + return anyIntegerToTime(src, func(i int64) time.Time { + return time.Unix(0, i) + }, &un.Time) +} + +func (un UnixNano) Unix() int64 { return zeroSafeUnix(un.Time, time.Time.Unix) } +func (un UnixNano) UnixMilli() int64 { return zeroSafeUnix(un.Time, time.Time.UnixMilli) } +func (un UnixNano) UnixMicro() int64 { return zeroSafeUnix(un.Time, time.Time.UnixMicro) } +func (un UnixNano) UnixNano() int64 { return zeroSafeUnix(un.Time, time.Time.UnixNano) } + +type Unix struct { + time.Time +} + +func (u Unix) MarshalJSON() ([]byte, error) { + return json.Marshal(u.Unix()) +} + +var _ sql.Scanner = &Unix{} +var _ driver.Valuer = Unix{} + +func (u *Unix) UnmarshalJSON(data []byte) error { + return parseTime(data, func(i int64) time.Time { + return time.Unix(i, 0) + }, &u.Time) +} + +func (u Unix) Value() (driver.Value, error) { + return u.Unix(), nil +} + +func (u *Unix) Scan(src any) error { + return anyIntegerToTime(src, func(i int64) time.Time { + return time.Unix(i, 0) + }, &u.Time) +} + +func (u Unix) Unix() int64 { return zeroSafeUnix(u.Time, time.Time.Unix) } +func (u Unix) UnixMilli() int64 { return zeroSafeUnix(u.Time, time.Time.UnixMilli) } +func (u Unix) UnixMicro() int64 { return zeroSafeUnix(u.Time, time.Time.UnixMicro) } +func (u Unix) UnixNano() int64 { return zeroSafeUnix(u.Time, time.Time.UnixNano) } diff --git a/vendor/go.mau.fi/util/jsontime/string.go b/vendor/go.mau.fi/util/jsontime/string.go new file mode 100644 index 00000000..c3729d58 --- /dev/null +++ b/vendor/go.mau.fi/util/jsontime/string.go @@ -0,0 +1,95 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package jsontime + +import ( + "encoding/json" + "strconv" + "time" +) + +func parseTimeString(data []byte, unixConv func(int64) time.Time, into *time.Time) error { + var strVal string + err := json.Unmarshal(data, &strVal) + if err != nil { + return err + } + val, err := strconv.ParseInt(strVal, 10, 64) + if err != nil { + return err + } + if val == 0 { + *into = time.Time{} + } else { + *into = unixConv(val) + } + return nil +} + +type UnixMilliString struct { + time.Time +} + +func (um UnixMilliString) MarshalJSON() ([]byte, error) { + if um.IsZero() { + return []byte{'"', '0', '"'}, nil + } + return json.Marshal(strconv.FormatInt(um.UnixMilli(), 10)) +} + +func (um *UnixMilliString) UnmarshalJSON(data []byte) error { + return parseTimeString(data, time.UnixMilli, &um.Time) +} + +type UnixMicroString struct { + time.Time +} + +func (um UnixMicroString) MarshalJSON() ([]byte, error) { + if um.IsZero() { + return []byte{'"', '0', '"'}, nil + } + return json.Marshal(strconv.FormatInt(um.UnixMicro(), 10)) +} + +func (um *UnixMicroString) UnmarshalJSON(data []byte) error { + return parseTimeString(data, time.UnixMicro, &um.Time) +} + +type UnixNanoString struct { + time.Time +} + +func (um UnixNanoString) MarshalJSON() ([]byte, error) { + if um.IsZero() { + return []byte{'"', '0', '"'}, nil + } + return json.Marshal(strconv.FormatInt(um.UnixNano(), 10)) +} + +func (um *UnixNanoString) UnmarshalJSON(data []byte) error { + return parseTimeString(data, func(i int64) time.Time { + return time.Unix(0, i) + }, &um.Time) +} + +type UnixString struct { + time.Time +} + +func (u UnixString) MarshalJSON() ([]byte, error) { + if u.IsZero() { + return []byte{'"', '0', '"'}, nil + } + return json.Marshal(strconv.FormatInt(u.Unix(), 10)) +} + +func (u *UnixString) UnmarshalJSON(data []byte) error { + return parseTimeString(data, func(i int64) time.Time { + return time.Unix(i, 0) + }, &u.Time) +} diff --git a/vendor/go.mau.fi/util/ptr/ptr.go b/vendor/go.mau.fi/util/ptr/ptr.go new file mode 100644 index 00000000..b04fa191 --- /dev/null +++ b/vendor/go.mau.fi/util/ptr/ptr.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package ptr + +// Clone creates a shallow copy of the given pointer. +func Clone[T any](val *T) *T { + if val == nil { + return nil + } + valCopy := *val + return &valCopy +} + +// Ptr returns a pointer to the given value. +func Ptr[T any](val T) *T { + return &val +} + +// NonZero returns a pointer to the given comparable value, unless the value is the type's zero value. +func NonZero[T comparable](val T) *T { + var zero T + return NonDefault(val, zero) +} + +// NonDefault returns a pointer to the first parameter, unless it is equal to the second parameter. +func NonDefault[T comparable](val, def T) *T { + if val == def { + return nil + } + return &val +} + +// Val returns the value of the given pointer, or the zero value of the type if the pointer is nil. +func Val[T any](ptr *T) (val T) { + if ptr != nil { + val = *ptr + } + return +} diff --git a/vendor/go.mau.fi/util/random/bytes.go b/vendor/go.mau.fi/util/random/bytes.go new file mode 100644 index 00000000..c3a706bd --- /dev/null +++ b/vendor/go.mau.fi/util/random/bytes.go @@ -0,0 +1,21 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package random + +import ( + "crypto/rand" +) + +// Bytes generates the given amount of random bytes using crypto/rand, and panics if it fails. +func Bytes(n int) []byte { + data := make([]byte, n) + _, err := rand.Read(data) + if err != nil { + panic(err) + } + return data +} diff --git a/vendor/go.mau.fi/util/random/string.go b/vendor/go.mau.fi/util/random/string.go new file mode 100644 index 00000000..5285c7ef --- /dev/null +++ b/vendor/go.mau.fi/util/random/string.go @@ -0,0 +1,127 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package random + +import ( + "encoding/binary" + "hash/crc32" + "strings" + + "go.mau.fi/util/exbytes" +) + +const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +// StringBytes generates a random string of the given length and returns it as a byte array. +func StringBytes(n int) []byte { + return StringBytesCharset(n, letters) +} + +// AppendSequence generates a random sequence of the given length using the given character set +// and appends it to the given output slice. +func AppendSequence[T any](n int, charset, output []T) []T { + if n <= 0 { + return output + } + if output == nil { + output = make([]T, 0, n) + } + // If risk of modulo bias is too high, use 32-bit integers as source instead of 16-bit. + if 65536%len(charset) < 200 { + input := Bytes(n * 2) + for i := 0; i < n; i++ { + output = append(output, charset[binary.BigEndian.Uint16(input[i*2:])%uint16(len(charset))]) + } + } else { + input := Bytes(n * 4) + for i := 0; i < n; i++ { + output = append(output, charset[binary.BigEndian.Uint32(input[i*4:])%uint32(len(charset))]) + } + } + return output +} + +// StringBytesCharset generates a random string of the given length using the given character set and returns it as a byte array. +// Note that the character set must be ASCII. For arbitrary Unicode, use [AppendSequence] with a `[]rune`. +func StringBytesCharset(n int, charset string) []byte { + if n <= 0 { + return []byte{} + } + input := Bytes(n * 2) + for i := 0; i < n; i++ { + // The risk of modulo bias is (65536 % len(charset)) / 65536. + // For the default charset, that's 2 in 65536 or 0.003 %. + input[i] = charset[binary.BigEndian.Uint16(input[i*2:])%uint16(len(charset))] + } + input = input[:n] + return input +} + +// String generates a random string of the given length. +func String(n int) string { + if n <= 0 { + return "" + } + return exbytes.UnsafeString(StringBytes(n)) +} + +// StringCharset generates a random string of the given length using the given character set. +// Note that the character set must be ASCII. For arbitrary Unicode, use [AppendSequence] with a `[]rune`. +func StringCharset(n int, charset string) string { + if n <= 0 { + return "" + } + return exbytes.UnsafeString(StringBytesCharset(n, charset)) +} + +func base62Encode(val uint32, minWidth int) []byte { + out := make([]byte, 0, minWidth) + for val > 0 { + out = append(out, letters[val%uint32(len(letters))]) + val /= 62 + } + if len(out) < minWidth { + paddedOut := make([]byte, minWidth) + copy(paddedOut[minWidth-len(out):], out) + for i := 0; i < minWidth-len(out); i++ { + paddedOut[i] = '0' + } + out = paddedOut + } + return out +} + +// Token generates a GitHub-style token with the given prefix, a random part, and a checksum at the end. +// The format is `prefix_random_checksum`. The checksum is always 6 characters. +func Token(namespace string, randomLength int) string { + token := make([]byte, len(namespace)+1+randomLength+1+6) + copy(token, namespace) + token[len(namespace)] = '_' + copy(token[len(namespace)+1:], StringBytes(randomLength)) + token[len(namespace)+randomLength+1] = '_' + checksum := base62Encode(crc32.ChecksumIEEE(token[:len(token)-7]), 6) + copy(token[len(token)-6:], checksum) + return exbytes.UnsafeString(token) +} + +// GetTokenPrefix parses the given token generated with Token, validates the checksum and returns the prefix namespace. +func GetTokenPrefix(token string) string { + parts := strings.Split(token, "_") + if len(parts) != 3 { + return "" + } + checksum := base62Encode(crc32.ChecksumIEEE([]byte(parts[0]+"_"+parts[1])), 6) + if string(checksum) != parts[2] { + return "" + } + return parts[0] +} + +// IsToken checks if the given token is a valid token generated with Token with the given namespace.. +func IsToken(namespace, token string) bool { + return GetTokenPrefix(token) == namespace +} diff --git a/vendor/go.mau.fi/util/retryafter/retryafter.go b/vendor/go.mau.fi/util/retryafter/retryafter.go new file mode 100644 index 00000000..57ec814d --- /dev/null +++ b/vendor/go.mau.fi/util/retryafter/retryafter.go @@ -0,0 +1,53 @@ +// Copyright (c) 2021 Dillon Dixon +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package retryafter contains a utility function for parsing the Retry-After HTTP header. +package retryafter + +import ( + "net/http" + "strconv" + "time" +) + +var now = time.Now + +// Parse parses the backoff time specified in the Retry-After header if present. +// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After. +// +// The second parameter is the fallback duration to use if the header is not present or invalid. +// +// Example: +// +// time.Sleep(retryafter.Parse(resp.Header.Get("Retry-After"), 5*time.Second)) +func Parse(retryAfter string, fallback time.Duration) time.Duration { + if retryAfter == "" { + return fallback + } else if t, err := time.Parse(http.TimeFormat, retryAfter); err == nil { + return t.Sub(now()) + } else if seconds, err := strconv.Atoi(retryAfter); err == nil { + return time.Duration(seconds) * time.Second + } + + return fallback +} + +// Should returns true if the given status code indicates that the request should be retried. +// +// if retryafter.Should(resp.StatusCode, true) { +// time.Sleep(retryafter.Parse(resp.Header.Get("Retry-After"), 5*time.Second)) +// } +func Should(statusCode int, retryOnRateLimit bool) bool { + switch statusCode { + case http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: + return true + case http.StatusTooManyRequests: + return retryOnRateLimit + default: + return false + } +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go new file mode 100644 index 00000000..048faef3 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -0,0 +1,93 @@ +// Copyright 2019 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 curve25519 provides an implementation of the X25519 function, which +// performs scalar multiplication on the elliptic curve known as Curve25519 +// according to [RFC 7748]. +// +// The curve25519 package is a wrapper for the X25519 implementation in the +// crypto/ecdh package. It is [frozen] and is not accepting new features. +// +// [RFC 7748]: https://datatracker.ietf.org/doc/html/rfc7748 +// [frozen]: https://go.dev/wiki/Frozen +package curve25519 + +import "crypto/ecdh" + +// ScalarMult sets dst to the product scalar * point. +// +// Deprecated: when provided a low-order point, ScalarMult will set dst to all +// zeroes, irrespective of the scalar. Instead, use the X25519 function, which +// will return an error. +func ScalarMult(dst, scalar, point *[32]byte) { + if _, err := x25519(dst, scalar[:], point[:]); err != nil { + // The only error condition for x25519 when the inputs are 32 bytes long + // is if the output would have been the all-zero value. + for i := range dst { + dst[i] = 0 + } + } +} + +// ScalarBaseMult sets dst to the product scalar * base where base is the +// standard generator. +// +// It is recommended to use the X25519 function with Basepoint instead, as +// copying into fixed size arrays can lead to unexpected bugs. +func ScalarBaseMult(dst, scalar *[32]byte) { + curve := ecdh.X25519() + priv, err := curve.NewPrivateKey(scalar[:]) + if err != nil { + panic("curve25519: " + err.Error()) + } + copy(dst[:], priv.PublicKey().Bytes()) +} + +const ( + // ScalarSize is the size of the scalar input to X25519. + ScalarSize = 32 + // PointSize is the size of the point input to X25519. + PointSize = 32 +) + +// Basepoint is the canonical Curve25519 generator. +var Basepoint []byte + +var basePoint = [32]byte{9} + +func init() { Basepoint = basePoint[:] } + +// X25519 returns the result of the scalar multiplication (scalar * point), +// according to RFC 7748, Section 5. scalar, point and the return value are +// slices of 32 bytes. +// +// scalar can be generated at random, for example with crypto/rand. point should +// be either Basepoint or the output of another X25519 call. +// +// If point is Basepoint (but not if it's a different slice with the same +// contents) a precomputed implementation might be used for performance. +func X25519(scalar, point []byte) ([]byte, error) { + // Outline the body of function, to let the allocation be inlined in the + // caller, and possibly avoid escaping to the heap. + var dst [32]byte + return x25519(&dst, scalar, point) +} + +func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { + curve := ecdh.X25519() + pub, err := curve.NewPublicKey(point) + if err != nil { + return nil, err + } + priv, err := curve.NewPrivateKey(scalar) + if err != nil { + return nil, err + } + out, err := priv.ECDH(pub) + if err != nil { + return nil, err + } + copy(dst[:], out) + return dst[:], nil +} diff --git a/vendor/golang.org/x/crypto/hkdf/hkdf.go b/vendor/golang.org/x/crypto/hkdf/hkdf.go new file mode 100644 index 00000000..3bee6629 --- /dev/null +++ b/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -0,0 +1,95 @@ +// Copyright 2014 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 hkdf implements the HMAC-based Extract-and-Expand Key Derivation +// Function (HKDF) as defined in RFC 5869. +// +// HKDF is a cryptographic key derivation function (KDF) with the goal of +// expanding limited input keying material into one or more cryptographically +// strong secret keys. +package hkdf + +import ( + "crypto/hmac" + "errors" + "hash" + "io" +) + +// Extract generates a pseudorandom key for use with Expand from an input secret +// and an optional independent salt. +// +// Only use this function if you need to reuse the extracted key with multiple +// Expand invocations and different context values. Most common scenarios, +// including the generation of multiple keys, should use New instead. +func Extract(hash func() hash.Hash, secret, salt []byte) []byte { + if salt == nil { + salt = make([]byte, hash().Size()) + } + extractor := hmac.New(hash, salt) + extractor.Write(secret) + return extractor.Sum(nil) +} + +type hkdf struct { + expander hash.Hash + size int + + info []byte + counter byte + + prev []byte + buf []byte +} + +func (f *hkdf) Read(p []byte) (int, error) { + // Check whether enough data can be generated + need := len(p) + remains := len(f.buf) + int(255-f.counter+1)*f.size + if remains < need { + return 0, errors.New("hkdf: entropy limit reached") + } + // Read any leftover from the buffer + n := copy(p, f.buf) + p = p[n:] + + // Fill the rest of the buffer + for len(p) > 0 { + if f.counter > 1 { + f.expander.Reset() + } + f.expander.Write(f.prev) + f.expander.Write(f.info) + f.expander.Write([]byte{f.counter}) + f.prev = f.expander.Sum(f.prev[:0]) + f.counter++ + + // Copy the new batch into p + f.buf = f.prev + n = copy(p, f.buf) + p = p[n:] + } + // Save leftovers for next run + f.buf = f.buf[n:] + + return need, nil +} + +// Expand returns a Reader, from which keys can be read, using the given +// pseudorandom key and optional context info, skipping the extraction step. +// +// The pseudorandomKey should have been generated by Extract, or be a uniformly +// random or pseudorandom cryptographically strong key. See RFC 5869, Section +// 3.3. Most common scenarios will want to use New instead. +func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { + expander := hmac.New(hash, pseudorandomKey) + return &hkdf{expander, expander.Size(), info, 1, nil, nil} +} + +// New returns a Reader, from which keys can be read, using the given hash, +// secret, salt and context info. Salt and info can be nil. +func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { + prk := Extract(hash, secret, salt) + return Expand(hash, prk, info) +} diff --git a/vendor/golang.org/x/exp/LICENSE b/vendor/golang.org/x/exp/LICENSE new file mode 100644 index 00000000..2a7cf70d --- /dev/null +++ b/vendor/golang.org/x/exp/LICENSE @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/exp/PATENTS b/vendor/golang.org/x/exp/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/vendor/golang.org/x/exp/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/exp/constraints/constraints.go b/vendor/golang.org/x/exp/constraints/constraints.go new file mode 100644 index 00000000..9d260bab --- /dev/null +++ b/vendor/golang.org/x/exp/constraints/constraints.go @@ -0,0 +1,54 @@ +// Copyright 2021 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 constraints defines a set of useful constraints to be used +// with type parameters. +package constraints + +import "cmp" + +// Signed is a constraint that permits any signed integer type. +// If future releases of Go add new predeclared signed integer types, +// this constraint will be modified to include them. +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// Unsigned is a constraint that permits any unsigned integer type. +// If future releases of Go add new predeclared unsigned integer types, +// this constraint will be modified to include them. +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +// Integer is a constraint that permits any integer type. +// If future releases of Go add new predeclared integer types, +// this constraint will be modified to include them. +type Integer interface { + Signed | Unsigned +} + +// Float is a constraint that permits any floating-point type. +// If future releases of Go add new predeclared floating-point types, +// this constraint will be modified to include them. +type Float interface { + ~float32 | ~float64 +} + +// Complex is a constraint that permits any complex numeric type. +// If future releases of Go add new predeclared complex numeric types, +// this constraint will be modified to include them. +type Complex interface { + ~complex64 | ~complex128 +} + +// Ordered is a constraint that permits any ordered type: any type +// that supports the operators < <= >= >. +// If future releases of Go add new ordered types, +// this constraint will be modified to include them. +// +// This type is redundant since Go 1.21 introduced [cmp.Ordered]. +// +//go:fix inline +type Ordered = cmp.Ordered diff --git a/vendor/golang.org/x/exp/maps/maps.go b/vendor/golang.org/x/exp/maps/maps.go new file mode 100644 index 00000000..4a9747ef --- /dev/null +++ b/vendor/golang.org/x/exp/maps/maps.go @@ -0,0 +1,86 @@ +// Copyright 2021 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 maps defines various functions useful with maps of any type. +package maps + +import "maps" + +// Keys returns the keys of the map m. +// The keys will be in an indeterminate order. +// +// The simplest true equivalent using the standard library is: +// +// slices.AppendSeq(make([]K, 0, len(m)), maps.Keys(m)) +func Keys[M ~map[K]V, K comparable, V any](m M) []K { + + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +} + +// Values returns the values of the map m. +// The values will be in an indeterminate order. +// +// The simplest true equivalent using the standard library is: +// +// slices.AppendSeq(make([]V, 0, len(m)), maps.Values(m)) +func Values[M ~map[K]V, K comparable, V any](m M) []V { + + r := make([]V, 0, len(m)) + for _, v := range m { + r = append(r, v) + } + return r +} + +// Equal reports whether two maps contain the same key/value pairs. +// Values are compared using ==. +// +//go:fix inline +func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool { + return maps.Equal(m1, m2) +} + +// EqualFunc is like Equal, but compares values using eq. +// Keys are still compared with ==. +// +//go:fix inline +func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool { + return maps.EqualFunc(m1, m2, eq) +} + +// Clear removes all entries from m, leaving it empty. +// +//go:fix inline +func Clear[M ~map[K]V, K comparable, V any](m M) { + clear(m) +} + +// Clone returns a copy of m. This is a shallow clone: +// the new keys and values are set using ordinary assignment. +// +//go:fix inline +func Clone[M ~map[K]V, K comparable, V any](m M) M { + return maps.Clone(m) +} + +// Copy copies all key/value pairs in src adding them to dst. +// When a key in src is already present in dst, +// the value in dst will be overwritten by the value associated +// with the key in src. +// +//go:fix inline +func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) { + maps.Copy(dst, src) +} + +// DeleteFunc deletes any key/value pairs from m for which del returns true. +// +//go:fix inline +func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) { + maps.DeleteFunc(m, del) +} diff --git a/vendor/golang.org/x/net/html/escape.go b/vendor/golang.org/x/net/html/escape.go index 04c6bec2..12f22737 100644 --- a/vendor/golang.org/x/net/html/escape.go +++ b/vendor/golang.org/x/net/html/escape.go @@ -299,7 +299,7 @@ func escape(w writer, s string) error { case '\r': esc = " " default: - panic("unrecognized escape character") + panic("html: unrecognized escape character") } s = s[i+1:] if _, err := w.WriteString(esc); err != nil { diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index 518ee4c9..88fc0056 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -136,7 +136,7 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { return -1 } default: - panic("unreachable") + panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } } switch s { @@ -179,7 +179,7 @@ func (p *parser) clearStackToContext(s scope) { return } default: - panic("unreachable") + panic(fmt.Sprintf("html: internal error: clearStackToContext unknown scope: %d", s)) } } } @@ -231,7 +231,14 @@ func (p *parser) addChild(n *Node) { } if n.Type == ElementNode { - p.oe = append(p.oe, n) + p.insertOpenElement(n) + } +} + +func (p *parser) insertOpenElement(n *Node) { + p.oe = append(p.oe, n) + if len(p.oe) > 512 { + panic("html: open stack of elements exceeds 512 nodes") } } @@ -810,7 +817,7 @@ func afterHeadIM(p *parser) bool { p.im = inFramesetIM return true case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title: - p.oe = append(p.oe, p.head) + p.insertOpenElement(p.head) defer p.oe.remove(p.head) return inHeadIM(p) case a.Head: @@ -1678,7 +1685,7 @@ func inTableBodyIM(p *parser) bool { return inTableIM(p) } -// Section 12.2.6.4.14. +// Section 13.2.6.4.14. func inRowIM(p *parser) bool { switch p.tok.Type { case StartTagToken: @@ -1690,7 +1697,9 @@ func inRowIM(p *parser) bool { p.im = inCellIM return true case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead, a.Tr: - if p.popUntil(tableScope, a.Tr) { + if p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() p.im = inTableBodyIM return false } @@ -1700,22 +1709,28 @@ func inRowIM(p *parser) bool { case EndTagToken: switch p.tok.DataAtom { case a.Tr: - if p.popUntil(tableScope, a.Tr) { + if p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() p.im = inTableBodyIM return true } // Ignore the token. return true case a.Table: - if p.popUntil(tableScope, a.Tr) { + if p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() p.im = inTableBodyIM return false } // Ignore the token. return true case a.Tbody, a.Tfoot, a.Thead: - if p.elementInScope(tableScope, p.tok.DataAtom) { - p.parseImpliedToken(EndTagToken, a.Tr, a.Tr.String()) + if p.elementInScope(tableScope, p.tok.DataAtom) && p.elementInScope(tableScope, a.Tr) { + p.clearStackToContext(tableRowScope) + p.oe.pop() + p.im = inTableBodyIM return false } // Ignore the token. @@ -2222,16 +2237,20 @@ func parseForeignContent(p *parser) bool { p.acknowledgeSelfClosingTag() } case EndTagToken: + if strings.EqualFold(p.oe[len(p.oe)-1].Data, p.tok.Data) { + p.oe = p.oe[:len(p.oe)-1] + return true + } for i := len(p.oe) - 1; i >= 0; i-- { - if p.oe[i].Namespace == "" { - return p.im(p) - } if strings.EqualFold(p.oe[i].Data, p.tok.Data) { p.oe = p.oe[:i] + return true + } + if i > 0 && p.oe[i-1].Namespace == "" { break } } - return true + return p.im(p) default: // Ignore the token. } @@ -2312,9 +2331,13 @@ func (p *parser) parseCurrentToken() { } } -func (p *parser) parse() error { +func (p *parser) parse() (err error) { + defer func() { + if panicErr := recover(); panicErr != nil { + err = fmt.Errorf("%s", panicErr) + } + }() // Iterate until EOF. Any other error will cause an early return. - var err error for err != io.EOF { // CDATA sections are allowed only in foreign content. n := p.oe.top() @@ -2343,6 +2366,8 @@ func (p *parser) parse() error { // s. Conversely, explicit s in r's data can be silently dropped, // with no corresponding node in the resulting tree. // +// Parse will reject HTML that is nested deeper than 512 elements. +// // The input is assumed to be UTF-8 encoded. func Parse(r io.Reader) (*Node, error) { return ParseWithOptions(r) diff --git a/vendor/golang.org/x/net/html/render.go b/vendor/golang.org/x/net/html/render.go index e8c12334..0157d89e 100644 --- a/vendor/golang.org/x/net/html/render.go +++ b/vendor/golang.org/x/net/html/render.go @@ -184,7 +184,7 @@ func render1(w writer, n *Node) error { return err } - // Add initial newline where there is danger of a newline beging ignored. + // Add initial newline where there is danger of a newline being ignored. if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") { switch n.Data { case "pre", "listing", "textarea": diff --git a/vendor/golang.org/x/net/http2/config.go b/vendor/golang.org/x/net/http2/config.go index ca645d9a..8a7a89d0 100644 --- a/vendor/golang.org/x/net/http2/config.go +++ b/vendor/golang.org/x/net/http2/config.go @@ -27,6 +27,7 @@ import ( // - If the resulting value is zero or out of range, use a default. type http2Config struct { MaxConcurrentStreams uint32 + StrictMaxConcurrentRequests bool MaxDecoderHeaderTableSize uint32 MaxEncoderHeaderTableSize uint32 MaxReadFrameSize uint32 @@ -55,7 +56,7 @@ func configFromServer(h1 *http.Server, h2 *Server) http2Config { PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites, CountError: h2.CountError, } - fillNetHTTPServerConfig(&conf, h1) + fillNetHTTPConfig(&conf, h1.HTTP2) setConfigDefaults(&conf, true) return conf } @@ -64,12 +65,13 @@ func configFromServer(h1 *http.Server, h2 *Server) http2Config { // (the net/http Transport). func configFromTransport(h2 *Transport) http2Config { conf := http2Config{ - MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, - MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, - MaxReadFrameSize: h2.MaxReadFrameSize, - SendPingTimeout: h2.ReadIdleTimeout, - PingTimeout: h2.PingTimeout, - WriteByteTimeout: h2.WriteByteTimeout, + StrictMaxConcurrentRequests: h2.StrictMaxConcurrentStreams, + MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, + MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, + MaxReadFrameSize: h2.MaxReadFrameSize, + SendPingTimeout: h2.ReadIdleTimeout, + PingTimeout: h2.PingTimeout, + WriteByteTimeout: h2.WriteByteTimeout, } // Unlike most config fields, where out-of-range values revert to the default, @@ -81,7 +83,7 @@ func configFromTransport(h2 *Transport) http2Config { } if h2.t1 != nil { - fillNetHTTPTransportConfig(&conf, h2.t1) + fillNetHTTPConfig(&conf, h2.t1.HTTP2) } setConfigDefaults(&conf, false) return conf @@ -120,3 +122,48 @@ func adjustHTTP1MaxHeaderSize(n int64) int64 { const typicalHeaders = 10 // conservative return n + typicalHeaders*perFieldOverhead } + +func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) { + if h2 == nil { + return + } + if h2.MaxConcurrentStreams != 0 { + conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) + } + if http2ConfigStrictMaxConcurrentRequests(h2) { + conf.StrictMaxConcurrentRequests = true + } + if h2.MaxEncoderHeaderTableSize != 0 { + conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize) + } + if h2.MaxDecoderHeaderTableSize != 0 { + conf.MaxDecoderHeaderTableSize = uint32(h2.MaxDecoderHeaderTableSize) + } + if h2.MaxConcurrentStreams != 0 { + conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) + } + if h2.MaxReadFrameSize != 0 { + conf.MaxReadFrameSize = uint32(h2.MaxReadFrameSize) + } + if h2.MaxReceiveBufferPerConnection != 0 { + conf.MaxUploadBufferPerConnection = int32(h2.MaxReceiveBufferPerConnection) + } + if h2.MaxReceiveBufferPerStream != 0 { + conf.MaxUploadBufferPerStream = int32(h2.MaxReceiveBufferPerStream) + } + if h2.SendPingTimeout != 0 { + conf.SendPingTimeout = h2.SendPingTimeout + } + if h2.PingTimeout != 0 { + conf.PingTimeout = h2.PingTimeout + } + if h2.WriteByteTimeout != 0 { + conf.WriteByteTimeout = h2.WriteByteTimeout + } + if h2.PermitProhibitedCipherSuites { + conf.PermitProhibitedCipherSuites = true + } + if h2.CountError != nil { + conf.CountError = h2.CountError + } +} diff --git a/vendor/golang.org/x/net/http2/config_go124.go b/vendor/golang.org/x/net/http2/config_go124.go deleted file mode 100644 index 5b516c55..00000000 --- a/vendor/golang.org/x/net/http2/config_go124.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 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. - -//go:build go1.24 - -package http2 - -import "net/http" - -// fillNetHTTPServerConfig sets fields in conf from srv.HTTP2. -func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) { - fillNetHTTPConfig(conf, srv.HTTP2) -} - -// fillNetHTTPTransportConfig sets fields in conf from tr.HTTP2. -func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) { - fillNetHTTPConfig(conf, tr.HTTP2) -} - -func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) { - if h2 == nil { - return - } - if h2.MaxConcurrentStreams != 0 { - conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) - } - if h2.MaxEncoderHeaderTableSize != 0 { - conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize) - } - if h2.MaxDecoderHeaderTableSize != 0 { - conf.MaxDecoderHeaderTableSize = uint32(h2.MaxDecoderHeaderTableSize) - } - if h2.MaxConcurrentStreams != 0 { - conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) - } - if h2.MaxReadFrameSize != 0 { - conf.MaxReadFrameSize = uint32(h2.MaxReadFrameSize) - } - if h2.MaxReceiveBufferPerConnection != 0 { - conf.MaxUploadBufferPerConnection = int32(h2.MaxReceiveBufferPerConnection) - } - if h2.MaxReceiveBufferPerStream != 0 { - conf.MaxUploadBufferPerStream = int32(h2.MaxReceiveBufferPerStream) - } - if h2.SendPingTimeout != 0 { - conf.SendPingTimeout = h2.SendPingTimeout - } - if h2.PingTimeout != 0 { - conf.PingTimeout = h2.PingTimeout - } - if h2.WriteByteTimeout != 0 { - conf.WriteByteTimeout = h2.WriteByteTimeout - } - if h2.PermitProhibitedCipherSuites { - conf.PermitProhibitedCipherSuites = true - } - if h2.CountError != nil { - conf.CountError = h2.CountError - } -} diff --git a/vendor/golang.org/x/net/http2/config_go125.go b/vendor/golang.org/x/net/http2/config_go125.go new file mode 100644 index 00000000..b4373fe3 --- /dev/null +++ b/vendor/golang.org/x/net/http2/config_go125.go @@ -0,0 +1,15 @@ +// Copyright 2025 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. + +//go:build !go1.26 + +package http2 + +import ( + "net/http" +) + +func http2ConfigStrictMaxConcurrentRequests(h2 *http.HTTP2Config) bool { + return false +} diff --git a/vendor/golang.org/x/net/http2/config_go126.go b/vendor/golang.org/x/net/http2/config_go126.go new file mode 100644 index 00000000..6b071c14 --- /dev/null +++ b/vendor/golang.org/x/net/http2/config_go126.go @@ -0,0 +1,15 @@ +// Copyright 2025 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. + +//go:build go1.26 + +package http2 + +import ( + "net/http" +) + +func http2ConfigStrictMaxConcurrentRequests(h2 *http.HTTP2Config) bool { + return h2.StrictMaxConcurrentRequests +} diff --git a/vendor/golang.org/x/net/http2/config_pre_go124.go b/vendor/golang.org/x/net/http2/config_pre_go124.go deleted file mode 100644 index 060fd6c6..00000000 --- a/vendor/golang.org/x/net/http2/config_pre_go124.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2024 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. - -//go:build !go1.24 - -package http2 - -import "net/http" - -// Pre-Go 1.24 fallback. -// The Server.HTTP2 and Transport.HTTP2 config fields were added in Go 1.24. - -func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) {} - -func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) {} diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go index db3264da..93bcaab0 100644 --- a/vendor/golang.org/x/net/http2/frame.go +++ b/vendor/golang.org/x/net/http2/frame.go @@ -347,7 +347,7 @@ func (fr *Framer) maxHeaderListSize() uint32 { func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) { // Write the FrameHeader. f.wbuf = append(f.wbuf[:0], - 0, // 3 bytes of length, filled in in endWrite + 0, // 3 bytes of length, filled in endWrite 0, 0, byte(ftype), @@ -1152,6 +1152,15 @@ type PriorityFrame struct { PriorityParam } +var defaultRFC9218Priority = PriorityParam{ + incremental: 0, + urgency: 3, +} + +// Note that HTTP/2 has had two different prioritization schemes, and +// PriorityParam struct below is a superset of both schemes. The exported +// symbols are from RFC 7540 and the non-exported ones are from RFC 9218. + // PriorityParam are the stream prioritzation parameters. type PriorityParam struct { // StreamDep is a 31-bit stream identifier for the @@ -1167,6 +1176,20 @@ type PriorityParam struct { // the spec, "Add one to the value to obtain a weight between // 1 and 256." Weight uint8 + + // "The urgency (u) parameter value is Integer (see Section 3.3.1 of + // [STRUCTURED-FIELDS]), between 0 and 7 inclusive, in descending order of + // priority. The default is 3." + urgency uint8 + + // "The incremental (i) parameter value is Boolean (see Section 3.3.6 of + // [STRUCTURED-FIELDS]). It indicates if an HTTP response can be processed + // incrementally, i.e., provide some meaningful output as chunks of the + // response arrive." + // + // We use uint8 (i.e. 0 is false, 1 is true) instead of bool so we can + // avoid unnecessary type conversions and because either type takes 1 byte. + incremental uint8 } func (p PriorityParam) IsZero() bool { diff --git a/vendor/golang.org/x/net/http2/gotrack.go b/vendor/golang.org/x/net/http2/gotrack.go index 9933c9f8..9921ca09 100644 --- a/vendor/golang.org/x/net/http2/gotrack.go +++ b/vendor/golang.org/x/net/http2/gotrack.go @@ -15,21 +15,32 @@ import ( "runtime" "strconv" "sync" + "sync/atomic" ) var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" +// Setting DebugGoroutines to false during a test to disable goroutine debugging +// results in race detector complaints when a test leaves goroutines running before +// returning. Tests shouldn't do this, of course, but when they do it generally shows +// up as infrequent, hard-to-debug flakes. (See #66519.) +// +// Disable goroutine debugging during individual tests with an atomic bool. +// (Note that it's safe to enable/disable debugging mid-test, so the actual race condition +// here is harmless.) +var disableDebugGoroutines atomic.Bool + type goroutineLock uint64 func newGoroutineLock() goroutineLock { - if !DebugGoroutines { + if !DebugGoroutines || disableDebugGoroutines.Load() { return 0 } return goroutineLock(curGoroutineID()) } func (g goroutineLock) check() { - if !DebugGoroutines { + if !DebugGoroutines || disableDebugGoroutines.Load() { return } if curGoroutineID() != uint64(g) { @@ -38,7 +49,7 @@ func (g goroutineLock) check() { } func (g goroutineLock) checkNotOn() { - if !DebugGoroutines { + if !DebugGoroutines || disableDebugGoroutines.Load() { return } if curGoroutineID() == uint64(g) { diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go index ea5ae629..105fe12f 100644 --- a/vendor/golang.org/x/net/http2/http2.go +++ b/vendor/golang.org/x/net/http2/http2.go @@ -15,7 +15,6 @@ package http2 // import "golang.org/x/net/http2" import ( "bufio" - "context" "crypto/tls" "errors" "fmt" @@ -35,7 +34,6 @@ var ( VerboseLogs bool logFrameWrites bool logFrameReads bool - inTests bool // Enabling extended CONNECT by causes browsers to attempt to use // WebSockets-over-HTTP/2. This results in problems when the server's websocket @@ -255,15 +253,13 @@ func (cw closeWaiter) Wait() { // idle memory usage with many connections. type bufferedWriter struct { _ incomparable - group synctestGroupInterface // immutable - conn net.Conn // immutable - bw *bufio.Writer // non-nil when data is buffered - byteTimeout time.Duration // immutable, WriteByteTimeout + conn net.Conn // immutable + bw *bufio.Writer // non-nil when data is buffered + byteTimeout time.Duration // immutable, WriteByteTimeout } -func newBufferedWriter(group synctestGroupInterface, conn net.Conn, timeout time.Duration) *bufferedWriter { +func newBufferedWriter(conn net.Conn, timeout time.Duration) *bufferedWriter { return &bufferedWriter{ - group: group, conn: conn, byteTimeout: timeout, } @@ -314,24 +310,18 @@ func (w *bufferedWriter) Flush() error { type bufferedWriterTimeoutWriter bufferedWriter func (w *bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) { - return writeWithByteTimeout(w.group, w.conn, w.byteTimeout, p) + return writeWithByteTimeout(w.conn, w.byteTimeout, p) } // writeWithByteTimeout writes to conn. // If more than timeout passes without any bytes being written to the connection, // the write fails. -func writeWithByteTimeout(group synctestGroupInterface, conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { +func writeWithByteTimeout(conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { if timeout <= 0 { return conn.Write(p) } for { - var now time.Time - if group == nil { - now = time.Now() - } else { - now = group.Now() - } - conn.SetWriteDeadline(now.Add(timeout)) + conn.SetWriteDeadline(time.Now().Add(timeout)) nn, err := conn.Write(p[n:]) n += nn if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) { @@ -417,14 +407,3 @@ func (s *sorter) SortStrings(ss []string) { // makes that struct also non-comparable, and generally doesn't add // any size (as long as it's first). type incomparable [0]func() - -// synctestGroupInterface is the methods of synctestGroup used by Server and Transport. -// It's defined as an interface here to let us keep synctestGroup entirely test-only -// and not a part of non-test builds. -type synctestGroupInterface interface { - Join() - Now() time.Time - NewTimer(d time.Duration) timer - AfterFunc(d time.Duration, f func()) timer - ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) -} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 51fca38f..bdc5520e 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -176,44 +176,15 @@ type Server struct { // so that we don't embed a Mutex in this struct, which will make the // struct non-copyable, which might break some callers. state *serverInternalState - - // Synchronization group used for testing. - // Outside of tests, this is nil. - group synctestGroupInterface -} - -func (s *Server) markNewGoroutine() { - if s.group != nil { - s.group.Join() - } -} - -func (s *Server) now() time.Time { - if s.group != nil { - return s.group.Now() - } - return time.Now() -} - -// newTimer creates a new time.Timer, or a synthetic timer in tests. -func (s *Server) newTimer(d time.Duration) timer { - if s.group != nil { - return s.group.NewTimer(d) - } - return timeTimer{time.NewTimer(d)} -} - -// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. -func (s *Server) afterFunc(d time.Duration, f func()) timer { - if s.group != nil { - return s.group.AfterFunc(d, f) - } - return timeTimer{time.AfterFunc(d, f)} } type serverInternalState struct { mu sync.Mutex activeConns map[*serverConn]struct{} + + // Pool of error channels. This is per-Server rather than global + // because channels can't be reused across synctest bubbles. + errChanPool sync.Pool } func (s *serverInternalState) registerConn(sc *serverConn) { @@ -245,6 +216,27 @@ func (s *serverInternalState) startGracefulShutdown() { s.mu.Unlock() } +// Global error channel pool used for uninitialized Servers. +// We use a per-Server pool when possible to avoid using channels across synctest bubbles. +var errChanPool = sync.Pool{ + New: func() any { return make(chan error, 1) }, +} + +func (s *serverInternalState) getErrChan() chan error { + if s == nil { + return errChanPool.Get().(chan error) // Server used without calling ConfigureServer + } + return s.errChanPool.Get().(chan error) +} + +func (s *serverInternalState) putErrChan(ch chan error) { + if s == nil { + errChanPool.Put(ch) // Server used without calling ConfigureServer + return + } + s.errChanPool.Put(ch) +} + // ConfigureServer adds HTTP/2 support to a net/http Server. // // The configuration conf may be nil. @@ -257,7 +249,10 @@ func ConfigureServer(s *http.Server, conf *Server) error { if conf == nil { conf = new(Server) } - conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})} + conf.state = &serverInternalState{ + activeConns: make(map[*serverConn]struct{}), + errChanPool: sync.Pool{New: func() any { return make(chan error, 1) }}, + } if h1, h2 := s, conf; h2.IdleTimeout == 0 { if h1.IdleTimeout != 0 { h2.IdleTimeout = h1.IdleTimeout @@ -423,6 +418,9 @@ func (o *ServeConnOpts) handler() http.Handler { // // The opts parameter is optional. If nil, default values are used. func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { + if opts == nil { + opts = &ServeConnOpts{} + } s.serveConn(c, opts, nil) } @@ -438,7 +436,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon conn: c, baseCtx: baseCtx, remoteAddrStr: c.RemoteAddr().String(), - bw: newBufferedWriter(s.group, c, conf.WriteByteTimeout), + bw: newBufferedWriter(c, conf.WriteByteTimeout), handler: opts.handler(), streams: make(map[uint32]*stream), readFrameCh: make(chan readFrameResult), @@ -638,11 +636,11 @@ type serverConn struct { pingSent bool sentPingData [8]byte goAwayCode ErrCode - shutdownTimer timer // nil until used - idleTimer timer // nil if unused + shutdownTimer *time.Timer // nil until used + idleTimer *time.Timer // nil if unused readIdleTimeout time.Duration pingTimeout time.Duration - readIdleTimer timer // nil if unused + readIdleTimer *time.Timer // nil if unused // Owned by the writeFrameAsync goroutine: headerWriteBuf bytes.Buffer @@ -687,12 +685,12 @@ type stream struct { flow outflow // limits writing from Handler to client inflow inflow // what the client is allowed to POST/etc to us state streamState - resetQueued bool // RST_STREAM queued for write; set by sc.resetStream - gotTrailerHeader bool // HEADER frame for trailers was seen - wroteHeaders bool // whether we wrote headers (not status 100) - readDeadline timer // nil if unused - writeDeadline timer // nil if unused - closeErr error // set before cw is closed + resetQueued bool // RST_STREAM queued for write; set by sc.resetStream + gotTrailerHeader bool // HEADER frame for trailers was seen + wroteHeaders bool // whether we wrote headers (not status 100) + readDeadline *time.Timer // nil if unused + writeDeadline *time.Timer // nil if unused + closeErr error // set before cw is closed trailer http.Header // accumulated trailers reqTrailer http.Header // handler's Request.Trailer @@ -848,7 +846,6 @@ type readFrameResult struct { // consumer is done with the frame. // It's run on its own goroutine. func (sc *serverConn) readFrames() { - sc.srv.markNewGoroutine() gate := make(chan struct{}) gateDone := func() { gate <- struct{}{} } for { @@ -881,7 +878,6 @@ type frameWriteResult struct { // At most one goroutine can be running writeFrameAsync at a time per // serverConn. func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) { - sc.srv.markNewGoroutine() var err error if wd == nil { err = wr.write.writeFrame(sc) @@ -965,22 +961,22 @@ func (sc *serverConn) serve(conf http2Config) { sc.setConnState(http.StateIdle) if sc.srv.IdleTimeout > 0 { - sc.idleTimer = sc.srv.afterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) + sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) defer sc.idleTimer.Stop() } if conf.SendPingTimeout > 0 { sc.readIdleTimeout = conf.SendPingTimeout - sc.readIdleTimer = sc.srv.afterFunc(conf.SendPingTimeout, sc.onReadIdleTimer) + sc.readIdleTimer = time.AfterFunc(conf.SendPingTimeout, sc.onReadIdleTimer) defer sc.readIdleTimer.Stop() } go sc.readFrames() // closed by defer sc.conn.Close above - settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer) + settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer) defer settingsTimer.Stop() - lastFrameTime := sc.srv.now() + lastFrameTime := time.Now() loopNum := 0 for { loopNum++ @@ -994,7 +990,7 @@ func (sc *serverConn) serve(conf http2Config) { case res := <-sc.wroteFrameCh: sc.wroteFrame(res) case res := <-sc.readFrameCh: - lastFrameTime = sc.srv.now() + lastFrameTime = time.Now() // Process any written frames before reading new frames from the client since a // written frame could have triggered a new stream to be started. if sc.writingFrameAsync { @@ -1077,7 +1073,7 @@ func (sc *serverConn) handlePingTimer(lastFrameReadTime time.Time) { } pingAt := lastFrameReadTime.Add(sc.readIdleTimeout) - now := sc.srv.now() + now := time.Now() if pingAt.After(now) { // We received frames since arming the ping timer. // Reset it for the next possible timeout. @@ -1141,10 +1137,10 @@ func (sc *serverConn) readPreface() error { errc <- nil } }() - timer := sc.srv.newTimer(prefaceTimeout) // TODO: configurable on *Server? + timer := time.NewTimer(prefaceTimeout) // TODO: configurable on *Server? defer timer.Stop() select { - case <-timer.C(): + case <-timer.C: return errPrefaceTimeout case err := <-errc: if err == nil { @@ -1156,10 +1152,6 @@ func (sc *serverConn) readPreface() error { } } -var errChanPool = sync.Pool{ - New: func() interface{} { return make(chan error, 1) }, -} - var writeDataPool = sync.Pool{ New: func() interface{} { return new(writeData) }, } @@ -1167,7 +1159,7 @@ var writeDataPool = sync.Pool{ // writeDataFromHandler writes DATA response frames from a handler on // the given stream. func (sc *serverConn) writeDataFromHandler(stream *stream, data []byte, endStream bool) error { - ch := errChanPool.Get().(chan error) + ch := sc.srv.state.getErrChan() writeArg := writeDataPool.Get().(*writeData) *writeArg = writeData{stream.id, data, endStream} err := sc.writeFrameFromHandler(FrameWriteRequest{ @@ -1199,7 +1191,7 @@ func (sc *serverConn) writeDataFromHandler(stream *stream, data []byte, endStrea return errStreamClosed } } - errChanPool.Put(ch) + sc.srv.state.putErrChan(ch) if frameWriteDone { writeDataPool.Put(writeArg) } @@ -1513,7 +1505,7 @@ func (sc *serverConn) goAway(code ErrCode) { func (sc *serverConn) shutDownIn(d time.Duration) { sc.serveG.check() - sc.shutdownTimer = sc.srv.afterFunc(d, sc.onShutdownTimer) + sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer) } func (sc *serverConn) resetStream(se StreamError) { @@ -2118,7 +2110,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { // (in Go 1.8), though. That's a more sane option anyway. if sc.hs.ReadTimeout > 0 { sc.conn.SetReadDeadline(time.Time{}) - st.readDeadline = sc.srv.afterFunc(sc.hs.ReadTimeout, st.onReadTimeout) + st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout) } return sc.scheduleHandler(id, rw, req, handler) @@ -2216,7 +2208,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream st.flow.add(sc.initialStreamSendWindowSize) st.inflow.init(sc.initialStreamRecvWindowSize) if sc.hs.WriteTimeout > 0 { - st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) + st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } sc.streams[id] = st @@ -2405,7 +2397,6 @@ func (sc *serverConn) handlerDone() { // Run on its own goroutine. func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) { - sc.srv.markNewGoroutine() defer sc.sendServeMsg(handlerDoneMsg) didPanic := true defer func() { @@ -2454,7 +2445,7 @@ func (sc *serverConn) writeHeaders(st *stream, headerData *writeResHeaders) erro // waiting for this frame to be written, so an http.Flush mid-handler // writes out the correct value of keys, before a handler later potentially // mutates it. - errc = errChanPool.Get().(chan error) + errc = sc.srv.state.getErrChan() } if err := sc.writeFrameFromHandler(FrameWriteRequest{ write: headerData, @@ -2466,7 +2457,7 @@ func (sc *serverConn) writeHeaders(st *stream, headerData *writeResHeaders) erro if errc != nil { select { case err := <-errc: - errChanPool.Put(errc) + sc.srv.state.putErrChan(errc) return err case <-sc.doneServing: return errClientDisconnected @@ -2573,7 +2564,7 @@ func (b *requestBody) Read(p []byte) (n int, err error) { if err == io.EOF { b.sawEOF = true } - if b.conn == nil && inTests { + if b.conn == nil { return } b.conn.noteBodyReadFromHandler(b.stream, n, err) @@ -2702,7 +2693,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { var date string if _, ok := rws.snapHeader["Date"]; !ok { // TODO(bradfitz): be faster here, like net/http? measure. - date = rws.conn.srv.now().UTC().Format(http.TimeFormat) + date = time.Now().UTC().Format(http.TimeFormat) } for _, v := range rws.snapHeader["Trailer"] { @@ -2824,7 +2815,7 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() { func (w *responseWriter) SetReadDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { + if !deadline.IsZero() && deadline.Before(time.Now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onReadTimeout() @@ -2840,9 +2831,9 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error { if deadline.IsZero() { st.readDeadline = nil } else if st.readDeadline == nil { - st.readDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onReadTimeout) + st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout) } else { - st.readDeadline.Reset(deadline.Sub(sc.srv.now())) + st.readDeadline.Reset(deadline.Sub(time.Now())) } }) return nil @@ -2850,7 +2841,7 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error { func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { + if !deadline.IsZero() && deadline.Before(time.Now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onWriteTimeout() @@ -2866,9 +2857,9 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { if deadline.IsZero() { st.writeDeadline = nil } else if st.writeDeadline == nil { - st.writeDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onWriteTimeout) + st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout) } else { - st.writeDeadline.Reset(deadline.Sub(sc.srv.now())) + st.writeDeadline.Reset(deadline.Sub(time.Now())) } }) return nil @@ -3147,7 +3138,7 @@ func (w *responseWriter) Push(target string, opts *http.PushOptions) error { method: opts.Method, url: u, header: cloneHeader(opts.Header), - done: errChanPool.Get().(chan error), + done: sc.srv.state.getErrChan(), } select { @@ -3164,7 +3155,7 @@ func (w *responseWriter) Push(target string, opts *http.PushOptions) error { case <-st.cw: return errStreamClosed case err := <-msg.done: - errChanPool.Put(msg.done) + sc.srv.state.putErrChan(msg.done) return err } } diff --git a/vendor/golang.org/x/net/http2/timer.go b/vendor/golang.org/x/net/http2/timer.go deleted file mode 100644 index 0b1c17b8..00000000 --- a/vendor/golang.org/x/net/http2/timer.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2024 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 http2 - -import "time" - -// A timer is a time.Timer, as an interface which can be replaced in tests. -type timer = interface { - C() <-chan time.Time - Reset(d time.Duration) bool - Stop() bool -} - -// timeTimer adapts a time.Timer to the timer interface. -type timeTimer struct { - *time.Timer -} - -func (t timeTimer) C() <-chan time.Time { return t.Timer.C } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index f26356b9..be759b60 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -193,50 +193,6 @@ type Transport struct { type transportTestHooks struct { newclientconn func(*ClientConn) - group synctestGroupInterface -} - -func (t *Transport) markNewGoroutine() { - if t != nil && t.transportTestHooks != nil { - t.transportTestHooks.group.Join() - } -} - -func (t *Transport) now() time.Time { - if t != nil && t.transportTestHooks != nil { - return t.transportTestHooks.group.Now() - } - return time.Now() -} - -func (t *Transport) timeSince(when time.Time) time.Duration { - if t != nil && t.transportTestHooks != nil { - return t.now().Sub(when) - } - return time.Since(when) -} - -// newTimer creates a new time.Timer, or a synthetic timer in tests. -func (t *Transport) newTimer(d time.Duration) timer { - if t.transportTestHooks != nil { - return t.transportTestHooks.group.NewTimer(d) - } - return timeTimer{time.NewTimer(d)} -} - -// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. -func (t *Transport) afterFunc(d time.Duration, f func()) timer { - if t.transportTestHooks != nil { - return t.transportTestHooks.group.AfterFunc(d, f) - } - return timeTimer{time.AfterFunc(d, f)} -} - -func (t *Transport) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { - if t.transportTestHooks != nil { - return t.transportTestHooks.group.ContextWithTimeout(ctx, d) - } - return context.WithTimeout(ctx, d) } func (t *Transport) maxHeaderListSize() uint32 { @@ -366,7 +322,7 @@ type ClientConn struct { readerErr error // set before readerDone is closed idleTimeout time.Duration // or 0 for never - idleTimer timer + idleTimer *time.Timer mu sync.Mutex // guards following cond *sync.Cond // hold mu; broadcast on flow/closed changes @@ -399,6 +355,7 @@ type ClientConn struct { readIdleTimeout time.Duration pingTimeout time.Duration extendedConnectAllowed bool + strictMaxConcurrentStreams bool // rstStreamPingsBlocked works around an unfortunate gRPC behavior. // gRPC strictly limits the number of PING frames that it will receive. @@ -534,14 +491,12 @@ func (cs *clientStream) closeReqBodyLocked() { cs.reqBodyClosed = make(chan struct{}) reqBodyClosed := cs.reqBodyClosed go func() { - cs.cc.t.markNewGoroutine() cs.reqBody.Close() close(reqBodyClosed) }() } type stickyErrWriter struct { - group synctestGroupInterface conn net.Conn timeout time.Duration err *error @@ -551,7 +506,7 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) { if *sew.err != nil { return 0, *sew.err } - n, err = writeWithByteTimeout(sew.group, sew.conn, sew.timeout, p) + n, err = writeWithByteTimeout(sew.conn, sew.timeout, p) *sew.err = err return n, err } @@ -650,9 +605,9 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) d := time.Second * time.Duration(backoff) - tm := t.newTimer(d) + tm := time.NewTimer(d) select { - case <-tm.C(): + case <-tm.C: t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) continue case <-req.Context().Done(): @@ -699,6 +654,7 @@ var ( errClientConnUnusable = errors.New("http2: client conn not usable") errClientConnNotEstablished = errors.New("http2: client conn could not be established") errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") + errClientConnForceClosed = errors.New("http2: client connection force closed via ClientConn.Close") ) // shouldRetryRequest is called by RoundTrip when a request fails to get @@ -829,7 +785,8 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro initialWindowSize: 65535, // spec default initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream, maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. - peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. + strictMaxConcurrentStreams: conf.StrictMaxConcurrentRequests, + peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. streams: make(map[uint32]*clientStream), singleUse: singleUse, seenSettingsChan: make(chan struct{}), @@ -838,14 +795,11 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro pingTimeout: conf.PingTimeout, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), - lastActive: t.now(), + lastActive: time.Now(), } - var group synctestGroupInterface if t.transportTestHooks != nil { - t.markNewGoroutine() t.transportTestHooks.newclientconn(cc) c = cc.tconn - group = t.group } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) @@ -857,7 +811,6 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro // TODO: adjust this writer size to account for frame size + // MTU + crypto/tls record padding. cc.bw = bufio.NewWriter(stickyErrWriter{ - group: group, conn: c, timeout: conf.WriteByteTimeout, err: &cc.werr, @@ -906,7 +859,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro // Start the idle timer after the connection is fully initialized. if d := t.idleConnTimeout(); d != 0 { cc.idleTimeout = d - cc.idleTimer = t.afterFunc(d, cc.onIdleTimeout) + cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout) } go cc.readLoop() @@ -917,7 +870,7 @@ func (cc *ClientConn) healthCheck() { pingTimeout := cc.pingTimeout // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. - ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout) + ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) defer cancel() cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) @@ -1067,7 +1020,7 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { return } var maxConcurrentOkay bool - if cc.t.StrictMaxConcurrentStreams { + if cc.strictMaxConcurrentStreams { // We'll tell the caller we can take a new request to // prevent the caller from dialing a new TCP // connection, but then we'll block later before @@ -1120,7 +1073,7 @@ func (cc *ClientConn) tooIdleLocked() bool { // times are compared based on their wall time. We don't want // to reuse a connection that's been sitting idle during // VM/laptop suspend if monotonic time was also frozen. - return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && cc.t.timeSince(cc.lastIdle.Round(0)) > cc.idleTimeout + return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout } // onIdleTimeout is called from a time.AfterFunc goroutine. It will @@ -1186,7 +1139,6 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { done := make(chan struct{}) cancelled := false // guarded by cc.mu go func() { - cc.t.markNewGoroutine() cc.mu.Lock() defer cc.mu.Unlock() for { @@ -1257,8 +1209,7 @@ func (cc *ClientConn) closeForError(err error) { // // In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. func (cc *ClientConn) Close() error { - err := errors.New("http2: client connection force closed via ClientConn.Close") - cc.closeForError(err) + cc.closeForError(errClientConnForceClosed) return nil } @@ -1427,7 +1378,6 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) // // It sends the request and performs post-request cleanup (closing Request.Body, etc.). func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)) { - cs.cc.t.markNewGoroutine() err := cs.writeRequest(req, streamf) cs.cleanupWriteRequest(err) } @@ -1558,9 +1508,9 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre var respHeaderTimer <-chan time.Time var respHeaderRecv chan struct{} if d := cc.responseHeaderTimeout(); d != 0 { - timer := cc.t.newTimer(d) + timer := time.NewTimer(d) defer timer.Stop() - respHeaderTimer = timer.C() + respHeaderTimer = timer.C respHeaderRecv = cs.respHeaderRecv } // Wait until the peer half-closes its end of the stream, @@ -1753,7 +1703,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { // Return a fatal error which aborts the retry loop. return errClientConnNotEstablished } - cc.lastActive = cc.t.now() + cc.lastActive = time.Now() if cc.closed || !cc.canTakeNewRequestLocked() { return errClientConnUnusable } @@ -2092,10 +2042,10 @@ func (cc *ClientConn) forgetStreamID(id uint32) { if len(cc.streams) != slen-1 { panic("forgetting unknown stream id") } - cc.lastActive = cc.t.now() + cc.lastActive = time.Now() if len(cc.streams) == 0 && cc.idleTimer != nil { cc.idleTimer.Reset(cc.idleTimeout) - cc.lastIdle = cc.t.now() + cc.lastIdle = time.Now() } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. @@ -2121,7 +2071,6 @@ type clientConnReadLoop struct { // readLoop runs in its own goroutine and reads and dispatches frames. func (cc *ClientConn) readLoop() { - cc.t.markNewGoroutine() rl := &clientConnReadLoop{cc: cc} defer rl.cleanup() cc.readerErr = rl.run() @@ -2188,9 +2137,9 @@ func (rl *clientConnReadLoop) cleanup() { if cc.idleTimeout > 0 && unusedWaitTime > cc.idleTimeout { unusedWaitTime = cc.idleTimeout } - idleTime := cc.t.now().Sub(cc.lastActive) + idleTime := time.Now().Sub(cc.lastActive) if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime && !cc.closedOnIdle { - cc.idleTimer = cc.t.afterFunc(unusedWaitTime-idleTime, func() { + cc.idleTimer = time.AfterFunc(unusedWaitTime-idleTime, func() { cc.t.connPool().MarkDead(cc) }) } else { @@ -2250,9 +2199,9 @@ func (rl *clientConnReadLoop) run() error { cc := rl.cc gotSettings := false readIdleTimeout := cc.readIdleTimeout - var t timer + var t *time.Timer if readIdleTimeout != 0 { - t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck) + t = time.AfterFunc(readIdleTimeout, cc.healthCheck) } for { f, err := cc.fr.ReadFrame() @@ -2998,7 +2947,6 @@ func (cc *ClientConn) Ping(ctx context.Context) error { var pingError error errc := make(chan struct{}) go func() { - cc.t.markNewGoroutine() cc.wmu.Lock() defer cc.wmu.Unlock() if pingError = cc.fr.WritePing(false, p); pingError != nil { @@ -3228,7 +3176,7 @@ func traceGotConn(req *http.Request, cc *ClientConn, reused bool) { cc.mu.Lock() ci.WasIdle = len(cc.streams) == 0 && reused if ci.WasIdle && !cc.lastActive.IsZero() { - ci.IdleTime = cc.t.timeSince(cc.lastActive) + ci.IdleTime = time.Since(cc.lastActive) } cc.mu.Unlock() diff --git a/vendor/golang.org/x/net/http2/writesched.go b/vendor/golang.org/x/net/http2/writesched.go index cc893adc..4d3890f9 100644 --- a/vendor/golang.org/x/net/http2/writesched.go +++ b/vendor/golang.org/x/net/http2/writesched.go @@ -42,6 +42,8 @@ type OpenStreamOptions struct { // PusherID is zero if the stream was initiated by the client. Otherwise, // PusherID names the stream that pushed the newly opened stream. PusherID uint32 + // priority is used to set the priority of the newly opened stream. + priority PriorityParam } // FrameWriteRequest is a request to write a frame. diff --git a/vendor/golang.org/x/net/http2/writesched_priority.go b/vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go similarity index 78% rename from vendor/golang.org/x/net/http2/writesched_priority.go rename to vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go index f6783339..6d24d6a1 100644 --- a/vendor/golang.org/x/net/http2/writesched_priority.go +++ b/vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go @@ -11,7 +11,7 @@ import ( ) // RFC 7540, Section 5.3.5: the default weight is 16. -const priorityDefaultWeight = 15 // 16 = 15 + 1 +const priorityDefaultWeightRFC7540 = 15 // 16 = 15 + 1 // PriorityWriteSchedulerConfig configures a priorityWriteScheduler. type PriorityWriteSchedulerConfig struct { @@ -66,8 +66,8 @@ func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler } } - ws := &priorityWriteScheduler{ - nodes: make(map[uint32]*priorityNode), + ws := &priorityWriteSchedulerRFC7540{ + nodes: make(map[uint32]*priorityNodeRFC7540), maxClosedNodesInTree: cfg.MaxClosedNodesInTree, maxIdleNodesInTree: cfg.MaxIdleNodesInTree, enableWriteThrottle: cfg.ThrottleOutOfOrderWrites, @@ -81,32 +81,32 @@ func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler return ws } -type priorityNodeState int +type priorityNodeStateRFC7540 int const ( - priorityNodeOpen priorityNodeState = iota - priorityNodeClosed - priorityNodeIdle + priorityNodeOpenRFC7540 priorityNodeStateRFC7540 = iota + priorityNodeClosedRFC7540 + priorityNodeIdleRFC7540 ) -// priorityNode is a node in an HTTP/2 priority tree. +// priorityNodeRFC7540 is a node in an HTTP/2 priority tree. // Each node is associated with a single stream ID. // See RFC 7540, Section 5.3. -type priorityNode struct { - q writeQueue // queue of pending frames to write - id uint32 // id of the stream, or 0 for the root of the tree - weight uint8 // the actual weight is weight+1, so the value is in [1,256] - state priorityNodeState // open | closed | idle - bytes int64 // number of bytes written by this node, or 0 if closed - subtreeBytes int64 // sum(node.bytes) of all nodes in this subtree +type priorityNodeRFC7540 struct { + q writeQueue // queue of pending frames to write + id uint32 // id of the stream, or 0 for the root of the tree + weight uint8 // the actual weight is weight+1, so the value is in [1,256] + state priorityNodeStateRFC7540 // open | closed | idle + bytes int64 // number of bytes written by this node, or 0 if closed + subtreeBytes int64 // sum(node.bytes) of all nodes in this subtree // These links form the priority tree. - parent *priorityNode - kids *priorityNode // start of the kids list - prev, next *priorityNode // doubly-linked list of siblings + parent *priorityNodeRFC7540 + kids *priorityNodeRFC7540 // start of the kids list + prev, next *priorityNodeRFC7540 // doubly-linked list of siblings } -func (n *priorityNode) setParent(parent *priorityNode) { +func (n *priorityNodeRFC7540) setParent(parent *priorityNodeRFC7540) { if n == parent { panic("setParent to self") } @@ -141,7 +141,7 @@ func (n *priorityNode) setParent(parent *priorityNode) { } } -func (n *priorityNode) addBytes(b int64) { +func (n *priorityNodeRFC7540) addBytes(b int64) { n.bytes += b for ; n != nil; n = n.parent { n.subtreeBytes += b @@ -154,7 +154,7 @@ func (n *priorityNode) addBytes(b int64) { // // f(n, openParent) takes two arguments: the node to visit, n, and a bool that is true // if any ancestor p of n is still open (ignoring the root node). -func (n *priorityNode) walkReadyInOrder(openParent bool, tmp *[]*priorityNode, f func(*priorityNode, bool) bool) bool { +func (n *priorityNodeRFC7540) walkReadyInOrder(openParent bool, tmp *[]*priorityNodeRFC7540, f func(*priorityNodeRFC7540, bool) bool) bool { if !n.q.empty() && f(n, openParent) { return true } @@ -165,7 +165,7 @@ func (n *priorityNode) walkReadyInOrder(openParent bool, tmp *[]*priorityNode, f // Don't consider the root "open" when updating openParent since // we can't send data frames on the root stream (only control frames). if n.id != 0 { - openParent = openParent || (n.state == priorityNodeOpen) + openParent = openParent || (n.state == priorityNodeOpenRFC7540) } // Common case: only one kid or all kids have the same weight. @@ -195,7 +195,7 @@ func (n *priorityNode) walkReadyInOrder(openParent bool, tmp *[]*priorityNode, f *tmp = append(*tmp, n.kids) n.kids.setParent(nil) } - sort.Sort(sortPriorityNodeSiblings(*tmp)) + sort.Sort(sortPriorityNodeSiblingsRFC7540(*tmp)) for i := len(*tmp) - 1; i >= 0; i-- { (*tmp)[i].setParent(n) // setParent inserts at the head of n.kids } @@ -207,11 +207,11 @@ func (n *priorityNode) walkReadyInOrder(openParent bool, tmp *[]*priorityNode, f return false } -type sortPriorityNodeSiblings []*priorityNode +type sortPriorityNodeSiblingsRFC7540 []*priorityNodeRFC7540 -func (z sortPriorityNodeSiblings) Len() int { return len(z) } -func (z sortPriorityNodeSiblings) Swap(i, k int) { z[i], z[k] = z[k], z[i] } -func (z sortPriorityNodeSiblings) Less(i, k int) bool { +func (z sortPriorityNodeSiblingsRFC7540) Len() int { return len(z) } +func (z sortPriorityNodeSiblingsRFC7540) Swap(i, k int) { z[i], z[k] = z[k], z[i] } +func (z sortPriorityNodeSiblingsRFC7540) Less(i, k int) bool { // Prefer the subtree that has sent fewer bytes relative to its weight. // See sections 5.3.2 and 5.3.4. wi, bi := float64(z[i].weight+1), float64(z[i].subtreeBytes) @@ -225,13 +225,13 @@ func (z sortPriorityNodeSiblings) Less(i, k int) bool { return bi/bk <= wi/wk } -type priorityWriteScheduler struct { +type priorityWriteSchedulerRFC7540 struct { // root is the root of the priority tree, where root.id = 0. // The root queues control frames that are not associated with any stream. - root priorityNode + root priorityNodeRFC7540 // nodes maps stream ids to priority tree nodes. - nodes map[uint32]*priorityNode + nodes map[uint32]*priorityNodeRFC7540 // maxID is the maximum stream id in nodes. maxID uint32 @@ -239,7 +239,7 @@ type priorityWriteScheduler struct { // lists of nodes that have been closed or are idle, but are kept in // the tree for improved prioritization. When the lengths exceed either // maxClosedNodesInTree or maxIdleNodesInTree, old nodes are discarded. - closedNodes, idleNodes []*priorityNode + closedNodes, idleNodes []*priorityNodeRFC7540 // From the config. maxClosedNodesInTree int @@ -248,19 +248,19 @@ type priorityWriteScheduler struct { enableWriteThrottle bool // tmp is scratch space for priorityNode.walkReadyInOrder to reduce allocations. - tmp []*priorityNode + tmp []*priorityNodeRFC7540 // pool of empty queues for reuse. queuePool writeQueuePool } -func (ws *priorityWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) { +func (ws *priorityWriteSchedulerRFC7540) OpenStream(streamID uint32, options OpenStreamOptions) { // The stream may be currently idle but cannot be opened or closed. if curr := ws.nodes[streamID]; curr != nil { - if curr.state != priorityNodeIdle { + if curr.state != priorityNodeIdleRFC7540 { panic(fmt.Sprintf("stream %d already opened", streamID)) } - curr.state = priorityNodeOpen + curr.state = priorityNodeOpenRFC7540 return } @@ -272,11 +272,11 @@ func (ws *priorityWriteScheduler) OpenStream(streamID uint32, options OpenStream if parent == nil { parent = &ws.root } - n := &priorityNode{ + n := &priorityNodeRFC7540{ q: *ws.queuePool.get(), id: streamID, - weight: priorityDefaultWeight, - state: priorityNodeOpen, + weight: priorityDefaultWeightRFC7540, + state: priorityNodeOpenRFC7540, } n.setParent(parent) ws.nodes[streamID] = n @@ -285,19 +285,19 @@ func (ws *priorityWriteScheduler) OpenStream(streamID uint32, options OpenStream } } -func (ws *priorityWriteScheduler) CloseStream(streamID uint32) { +func (ws *priorityWriteSchedulerRFC7540) CloseStream(streamID uint32) { if streamID == 0 { panic("violation of WriteScheduler interface: cannot close stream 0") } if ws.nodes[streamID] == nil { panic(fmt.Sprintf("violation of WriteScheduler interface: unknown stream %d", streamID)) } - if ws.nodes[streamID].state != priorityNodeOpen { + if ws.nodes[streamID].state != priorityNodeOpenRFC7540 { panic(fmt.Sprintf("violation of WriteScheduler interface: stream %d already closed", streamID)) } n := ws.nodes[streamID] - n.state = priorityNodeClosed + n.state = priorityNodeClosedRFC7540 n.addBytes(-n.bytes) q := n.q @@ -310,7 +310,7 @@ func (ws *priorityWriteScheduler) CloseStream(streamID uint32) { } } -func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) { +func (ws *priorityWriteSchedulerRFC7540) AdjustStream(streamID uint32, priority PriorityParam) { if streamID == 0 { panic("adjustPriority on root") } @@ -324,11 +324,11 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit return } ws.maxID = streamID - n = &priorityNode{ + n = &priorityNodeRFC7540{ q: *ws.queuePool.get(), id: streamID, - weight: priorityDefaultWeight, - state: priorityNodeIdle, + weight: priorityDefaultWeightRFC7540, + state: priorityNodeIdleRFC7540, } n.setParent(&ws.root) ws.nodes[streamID] = n @@ -340,7 +340,7 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit parent := ws.nodes[priority.StreamDep] if parent == nil { n.setParent(&ws.root) - n.weight = priorityDefaultWeight + n.weight = priorityDefaultWeightRFC7540 return } @@ -381,8 +381,8 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit n.weight = priority.Weight } -func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) { - var n *priorityNode +func (ws *priorityWriteSchedulerRFC7540) Push(wr FrameWriteRequest) { + var n *priorityNodeRFC7540 if wr.isControl() { n = &ws.root } else { @@ -401,8 +401,8 @@ func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) { n.q.push(wr) } -func (ws *priorityWriteScheduler) Pop() (wr FrameWriteRequest, ok bool) { - ws.root.walkReadyInOrder(false, &ws.tmp, func(n *priorityNode, openParent bool) bool { +func (ws *priorityWriteSchedulerRFC7540) Pop() (wr FrameWriteRequest, ok bool) { + ws.root.walkReadyInOrder(false, &ws.tmp, func(n *priorityNodeRFC7540, openParent bool) bool { limit := int32(math.MaxInt32) if openParent { limit = ws.writeThrottleLimit @@ -428,7 +428,7 @@ func (ws *priorityWriteScheduler) Pop() (wr FrameWriteRequest, ok bool) { return wr, ok } -func (ws *priorityWriteScheduler) addClosedOrIdleNode(list *[]*priorityNode, maxSize int, n *priorityNode) { +func (ws *priorityWriteSchedulerRFC7540) addClosedOrIdleNode(list *[]*priorityNodeRFC7540, maxSize int, n *priorityNodeRFC7540) { if maxSize == 0 { return } @@ -442,7 +442,7 @@ func (ws *priorityWriteScheduler) addClosedOrIdleNode(list *[]*priorityNode, max *list = append(*list, n) } -func (ws *priorityWriteScheduler) removeNode(n *priorityNode) { +func (ws *priorityWriteSchedulerRFC7540) removeNode(n *priorityNodeRFC7540) { for n.kids != nil { n.kids.setParent(n.parent) } diff --git a/vendor/golang.org/x/net/http2/writesched_priority_rfc9128.go b/vendor/golang.org/x/net/http2/writesched_priority_rfc9128.go new file mode 100644 index 00000000..9b5b8808 --- /dev/null +++ b/vendor/golang.org/x/net/http2/writesched_priority_rfc9128.go @@ -0,0 +1,209 @@ +// Copyright 2025 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 http2 + +import ( + "fmt" + "math" +) + +type streamMetadata struct { + location *writeQueue + priority PriorityParam +} + +type priorityWriteSchedulerRFC9218 struct { + // control contains control frames (SETTINGS, PING, etc.). + control writeQueue + + // heads contain the head of a circular list of streams. + // We put these heads within a nested array that represents urgency and + // incremental, as defined in + // https://www.rfc-editor.org/rfc/rfc9218.html#name-priority-parameters. + // 8 represents u=0 up to u=7, and 2 represents i=false and i=true. + heads [8][2]*writeQueue + + // streams contains a mapping between each stream ID and their metadata, so + // we can quickly locate them when needing to, for example, adjust their + // priority. + streams map[uint32]streamMetadata + + // queuePool are empty queues for reuse. + queuePool writeQueuePool + + // prioritizeIncremental is used to determine whether we should prioritize + // incremental streams or not, when urgency is the same in a given Pop() + // call. + prioritizeIncremental bool +} + +func newPriorityWriteSchedulerRFC9128() WriteScheduler { + ws := &priorityWriteSchedulerRFC9218{ + streams: make(map[uint32]streamMetadata), + } + return ws +} + +func (ws *priorityWriteSchedulerRFC9218) OpenStream(streamID uint32, opt OpenStreamOptions) { + if ws.streams[streamID].location != nil { + panic(fmt.Errorf("stream %d already opened", streamID)) + } + q := ws.queuePool.get() + ws.streams[streamID] = streamMetadata{ + location: q, + priority: opt.priority, + } + + u, i := opt.priority.urgency, opt.priority.incremental + if ws.heads[u][i] == nil { + ws.heads[u][i] = q + q.next = q + q.prev = q + } else { + // Queues are stored in a ring. + // Insert the new stream before ws.head, putting it at the end of the list. + q.prev = ws.heads[u][i].prev + q.next = ws.heads[u][i] + q.prev.next = q + q.next.prev = q + } +} + +func (ws *priorityWriteSchedulerRFC9218) CloseStream(streamID uint32) { + metadata := ws.streams[streamID] + q, u, i := metadata.location, metadata.priority.urgency, metadata.priority.incremental + if q == nil { + return + } + if q.next == q { + // This was the only open stream. + ws.heads[u][i] = nil + } else { + q.prev.next = q.next + q.next.prev = q.prev + if ws.heads[u][i] == q { + ws.heads[u][i] = q.next + } + } + delete(ws.streams, streamID) + ws.queuePool.put(q) +} + +func (ws *priorityWriteSchedulerRFC9218) AdjustStream(streamID uint32, priority PriorityParam) { + metadata := ws.streams[streamID] + q, u, i := metadata.location, metadata.priority.urgency, metadata.priority.incremental + if q == nil { + return + } + + // Remove stream from current location. + if q.next == q { + // This was the only open stream. + ws.heads[u][i] = nil + } else { + q.prev.next = q.next + q.next.prev = q.prev + if ws.heads[u][i] == q { + ws.heads[u][i] = q.next + } + } + + // Insert stream to the new queue. + u, i = priority.urgency, priority.incremental + if ws.heads[u][i] == nil { + ws.heads[u][i] = q + q.next = q + q.prev = q + } else { + // Queues are stored in a ring. + // Insert the new stream before ws.head, putting it at the end of the list. + q.prev = ws.heads[u][i].prev + q.next = ws.heads[u][i] + q.prev.next = q + q.next.prev = q + } + + // Update the metadata. + ws.streams[streamID] = streamMetadata{ + location: q, + priority: priority, + } +} + +func (ws *priorityWriteSchedulerRFC9218) Push(wr FrameWriteRequest) { + if wr.isControl() { + ws.control.push(wr) + return + } + q := ws.streams[wr.StreamID()].location + if q == nil { + // This is a closed stream. + // wr should not be a HEADERS or DATA frame. + // We push the request onto the control queue. + if wr.DataSize() > 0 { + panic("add DATA on non-open stream") + } + ws.control.push(wr) + return + } + q.push(wr) +} + +func (ws *priorityWriteSchedulerRFC9218) Pop() (FrameWriteRequest, bool) { + // Control and RST_STREAM frames first. + if !ws.control.empty() { + return ws.control.shift(), true + } + + // On the next Pop(), we want to prioritize incremental if we prioritized + // non-incremental request of the same urgency this time. Vice-versa. + // i.e. when there are incremental and non-incremental requests at the same + // priority, we give 50% of our bandwidth to the incremental ones in + // aggregate and 50% to the first non-incremental one (since + // non-incremental streams do not use round-robin writes). + ws.prioritizeIncremental = !ws.prioritizeIncremental + + // Always prioritize lowest u (i.e. highest urgency level). + for u := range ws.heads { + for i := range ws.heads[u] { + // When we want to prioritize incremental, we try to pop i=true + // first before i=false when u is the same. + if ws.prioritizeIncremental { + i = (i + 1) % 2 + } + q := ws.heads[u][i] + if q == nil { + continue + } + for { + if wr, ok := q.consume(math.MaxInt32); ok { + if i == 1 { + // For incremental streams, we update head to q.next so + // we can round-robin between multiple streams that can + // immediately benefit from partial writes. + ws.heads[u][i] = q.next + } else { + // For non-incremental streams, we try to finish one to + // completion rather than doing round-robin. However, + // we update head here so that if q.consume() is !ok + // (e.g. the stream has no more frame to consume), head + // is updated to the next q that has frames to consume + // on future iterations. This way, we do not prioritize + // writing to unavailable stream on next Pop() calls, + // preventing head-of-line blocking. + ws.heads[u][i] = q + } + return wr, true + } + q = q.next + if q == ws.heads[u][i] { + break + } + } + + } + } + return FrameWriteRequest{}, false +} diff --git a/vendor/golang.org/x/net/http2/writesched_roundrobin.go b/vendor/golang.org/x/net/http2/writesched_roundrobin.go index 54fe8632..737cff9e 100644 --- a/vendor/golang.org/x/net/http2/writesched_roundrobin.go +++ b/vendor/golang.org/x/net/http2/writesched_roundrobin.go @@ -25,7 +25,7 @@ type roundRobinWriteScheduler struct { } // newRoundRobinWriteScheduler constructs a new write scheduler. -// The round robin scheduler priorizes control frames +// The round robin scheduler prioritizes control frames // like SETTINGS and PING over DATA frames. // When there are no control frames to send, it performs a round-robin // selection from the ready streams. diff --git a/vendor/golang.org/x/net/internal/httpcommon/request.go b/vendor/golang.org/x/net/internal/httpcommon/request.go index 4b705531..1e10f89e 100644 --- a/vendor/golang.org/x/net/internal/httpcommon/request.go +++ b/vendor/golang.org/x/net/internal/httpcommon/request.go @@ -51,7 +51,7 @@ type EncodeHeadersParam struct { DefaultUserAgent string } -// EncodeHeadersParam is the result of EncodeHeaders. +// EncodeHeadersResult is the result of EncodeHeaders. type EncodeHeadersResult struct { HasBody bool HasTrailers bool @@ -399,7 +399,7 @@ type ServerRequestResult struct { // If the request should be rejected, this is a short string suitable for passing // to the http2 package's CountError function. - // It might be a bit odd to return errors this way rather than returing an error, + // It might be a bit odd to return errors this way rather than returning an error, // but this ensures we don't forget to include a CountError reason. InvalidReason string } diff --git a/vendor/golang.org/x/net/internal/socks/socks.go b/vendor/golang.org/x/net/internal/socks/socks.go index 84fcc32b..8eedb84c 100644 --- a/vendor/golang.org/x/net/internal/socks/socks.go +++ b/vendor/golang.org/x/net/internal/socks/socks.go @@ -297,7 +297,7 @@ func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, b = append(b, up.Username...) b = append(b, byte(len(up.Password))) b = append(b, up.Password...) - // TODO(mikio): handle IO deadlines and cancelation if + // TODO(mikio): handle IO deadlines and cancellation if // necessary if _, err := rw.Write(b); err != nil { return err diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go index 63541994..34c9ae76 100644 --- a/vendor/golang.org/x/sys/cpu/cpu.go +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -92,6 +92,9 @@ var ARM64 struct { HasSHA2 bool // SHA2 hardware implementation HasCRC32 bool // CRC32 hardware implementation HasATOMICS bool // Atomic memory operation instruction set + HasHPDS bool // Hierarchical permission disables in translations tables + HasLOR bool // Limited ordering regions + HasPAN bool // Privileged access never HasFPHP bool // Half precision floating-point instruction set HasASIMDHP bool // Advanced SIMD half precision instruction set HasCPUID bool // CPUID identification scheme registers diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_arm64.go index af2aa99f..f449c679 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.go @@ -65,10 +65,10 @@ func setMinimalFeatures() { func readARM64Registers() { Initialized = true - parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0()) + parseARM64SystemRegisters(getisar0(), getisar1(), getmmfr1(), getpfr0()) } -func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { +func parseARM64SystemRegisters(isar0, isar1, mmfr1, pfr0 uint64) { // ID_AA64ISAR0_EL1 switch extractBits(isar0, 4, 7) { case 1: @@ -152,6 +152,22 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { ARM64.HasI8MM = true } + // ID_AA64MMFR1_EL1 + switch extractBits(mmfr1, 12, 15) { + case 1, 2: + ARM64.HasHPDS = true + } + + switch extractBits(mmfr1, 16, 19) { + case 1: + ARM64.HasLOR = true + } + + switch extractBits(mmfr1, 20, 23) { + case 1, 2, 3: + ARM64.HasPAN = true + } + // ID_AA64PFR0_EL1 switch extractBits(pfr0, 16, 19) { case 0: diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_arm64.s index 22cc9984..a4f24b3b 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_arm64.s +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.s @@ -9,31 +9,34 @@ // func getisar0() uint64 TEXT ·getisar0(SB),NOSPLIT,$0-8 // get Instruction Set Attributes 0 into x0 - // mrs x0, ID_AA64ISAR0_EL1 = d5380600 - WORD $0xd5380600 + MRS ID_AA64ISAR0_EL1, R0 MOVD R0, ret+0(FP) RET // func getisar1() uint64 TEXT ·getisar1(SB),NOSPLIT,$0-8 // get Instruction Set Attributes 1 into x0 - // mrs x0, ID_AA64ISAR1_EL1 = d5380620 - WORD $0xd5380620 + MRS ID_AA64ISAR1_EL1, R0 + MOVD R0, ret+0(FP) + RET + +// func getmmfr1() uint64 +TEXT ·getmmfr1(SB),NOSPLIT,$0-8 + // get Memory Model Feature Register 1 into x0 + MRS ID_AA64MMFR1_EL1, R0 MOVD R0, ret+0(FP) RET // func getpfr0() uint64 TEXT ·getpfr0(SB),NOSPLIT,$0-8 // get Processor Feature Register 0 into x0 - // mrs x0, ID_AA64PFR0_EL1 = d5380400 - WORD $0xd5380400 + MRS ID_AA64PFR0_EL1, R0 MOVD R0, ret+0(FP) RET // func getzfr0() uint64 TEXT ·getzfr0(SB),NOSPLIT,$0-8 // get SVE Feature Register 0 into x0 - // mrs x0, ID_AA64ZFR0_EL1 = d5380480 - WORD $0xd5380480 + MRS ID_AA64ZFR0_EL1, R0 MOVD R0, ret+0(FP) RET diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go index 6ac6e1ef..e3fc5a8d 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_arm64.go @@ -8,5 +8,6 @@ package cpu func getisar0() uint64 func getisar1() uint64 +func getmmfr1() uint64 func getpfr0() uint64 func getzfr0() uint64 diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go index 7f194678..8df2079e 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go @@ -8,4 +8,5 @@ package cpu func getisar0() uint64 { return 0 } func getisar1() uint64 { return 0 } +func getmmfr1() uint64 { return 0 } func getpfr0() uint64 { return 0 } diff --git a/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go index ebfb3fc8..19aea063 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_netbsd_arm64.go @@ -167,7 +167,7 @@ func doinit() { setMinimalFeatures() return } - parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64pfr0) + parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64mmfr1, cpuid.aa64pfr0) Initialized = true } diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go index 85b64d5c..87fd3a77 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go @@ -59,7 +59,7 @@ func doinit() { if !ok { return } - parseARM64SystemRegisters(isar0, isar1, 0) + parseARM64SystemRegisters(isar0, isar1, 0, 0) Initialized = true } diff --git a/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go b/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go deleted file mode 100644 index 73687de7..00000000 --- a/vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 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. - -//go:build go1.5 - -package plan9 - -import "syscall" - -func fixwd() { - syscall.Fixwd() -} - -func Getwd() (wd string, err error) { - return syscall.Getwd() -} - -func Chdir(path string) error { - return syscall.Chdir(path) -} diff --git a/vendor/golang.org/x/sys/plan9/pwd_plan9.go b/vendor/golang.org/x/sys/plan9/pwd_plan9.go index fb945821..7a76489d 100644 --- a/vendor/golang.org/x/sys/plan9/pwd_plan9.go +++ b/vendor/golang.org/x/sys/plan9/pwd_plan9.go @@ -2,22 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !go1.5 - package plan9 +import "syscall" + func fixwd() { + syscall.Fixwd() } func Getwd() (wd string, err error) { - fd, err := open(".", O_RDONLY) - if err != nil { - return "", err - } - defer Close(fd) - return Fd2path(fd) + return syscall.Getwd() } func Chdir(path string) error { - return chdir(path) + return syscall.Chdir(path) } diff --git a/vendor/golang.org/x/sys/unix/affinity_linux.go b/vendor/golang.org/x/sys/unix/affinity_linux.go index 6e5c81ac..3ea47038 100644 --- a/vendor/golang.org/x/sys/unix/affinity_linux.go +++ b/vendor/golang.org/x/sys/unix/affinity_linux.go @@ -38,8 +38,15 @@ func SchedSetaffinity(pid int, set *CPUSet) error { // Zero clears the set s, so that it contains no CPUs. func (s *CPUSet) Zero() { + clear(s[:]) +} + +// Fill adds all possible CPU bits to the set s. On Linux, [SchedSetaffinity] +// will silently ignore any invalid CPU bits in [CPUSet] so this is an +// efficient way of resetting the CPU affinity of a process. +func (s *CPUSet) Fill() { for i := range s { - s[i] = 0 + s[i] = ^cpuMask(0) } } diff --git a/vendor/golang.org/x/sys/unix/fdset.go b/vendor/golang.org/x/sys/unix/fdset.go index 9e83d18c..62ed1264 100644 --- a/vendor/golang.org/x/sys/unix/fdset.go +++ b/vendor/golang.org/x/sys/unix/fdset.go @@ -23,7 +23,5 @@ func (fds *FdSet) IsSet(fd int) bool { // Zero clears the set fds. func (fds *FdSet) Zero() { - for i := range fds.Bits { - fds.Bits[i] = 0 - } + clear(fds.Bits[:]) } diff --git a/vendor/golang.org/x/sys/unix/ifreq_linux.go b/vendor/golang.org/x/sys/unix/ifreq_linux.go index 848840ae..309f5a2b 100644 --- a/vendor/golang.org/x/sys/unix/ifreq_linux.go +++ b/vendor/golang.org/x/sys/unix/ifreq_linux.go @@ -111,9 +111,7 @@ func (ifr *Ifreq) SetUint32(v uint32) { // clear zeroes the ifreq's union field to prevent trailing garbage data from // being sent to the kernel if an ifreq is reused. func (ifr *Ifreq) clear() { - for i := range ifr.raw.Ifru { - ifr.raw.Ifru[i] = 0 - } + clear(ifr.raw.Ifru[:]) } // TODO(mdlayher): export as IfreqData? For now we can provide helpers such as diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index e6f31d37..d0ed6119 100644 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -49,6 +49,7 @@ esac if [[ "$GOOS" = "linux" ]]; then # Use the Docker-based build system # Files generated through docker (use $cmd so you can Ctl-C the build or run) + set -e $cmd docker build --tag generate:$GOOS $GOOS $cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS exit diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index d1c8b264..42517077 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -226,6 +226,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -529,6 +530,7 @@ ccflags="$@" $2 ~ /^O[CNPFPL][A-Z]+[^_][A-Z]+$/ || $2 ~ /^(NL|CR|TAB|BS|VT|FF)DLY$/ || $2 ~ /^(NL|CR|TAB|BS|VT|FF)[0-9]$/ || + $2 ~ /^(DT|EI|ELF|EV|NN|NT|PF|SHF|SHN|SHT|STB|STT|VER)_/ || $2 ~ /^O?XTABS$/ || $2 ~ /^TC[IO](ON|OFF)$/ || $2 ~ /^IN_/ || diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 4958a657..06c0eea6 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -801,9 +801,7 @@ func (sa *SockaddrPPPoE) sockaddr() (unsafe.Pointer, _Socklen, error) { // one. The kernel expects SID to be in network byte order. binary.BigEndian.PutUint16(sa.raw[6:8], sa.SID) copy(sa.raw[8:14], sa.Remote) - for i := 14; i < 14+IFNAMSIZ; i++ { - sa.raw[i] = 0 - } + clear(sa.raw[14 : 14+IFNAMSIZ]) copy(sa.raw[14:], sa.Dev) return unsafe.Pointer(&sa.raw), SizeofSockaddrPPPoX, nil } @@ -2645,3 +2643,9 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) { //sys Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) //sys Mseal(b []byte, flags uint) (err error) + +//sys setMemPolicy(mode int, mask *CPUSet, size int) (err error) = SYS_SET_MEMPOLICY + +func SetMemPolicy(mode int, mask *CPUSet) error { + return setMemPolicy(mode, mask, _CPU_SETSIZE) +} diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/vendor/golang.org/x/sys/unix/syscall_netbsd.go index 88162099..34a46769 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd.go @@ -248,6 +248,23 @@ func Statvfs(path string, buf *Statvfs_t) (err error) { return Statvfs1(path, buf, ST_WAIT) } +func Getvfsstat(buf []Statvfs_t, flags int) (n int, err error) { + var ( + _p0 unsafe.Pointer + bufsize uintptr + ) + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + bufsize = unsafe.Sizeof(Statvfs_t{}) * uintptr(len(buf)) + } + r0, _, e1 := Syscall(SYS_GETVFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + /* * Exposed directly */ diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index abc39554..18a3d9bd 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -629,7 +629,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Kill(pid int, signum syscall.Signal) (err error) //sys Lchown(path string, uid int, gid int) (err error) //sys Link(path string, link string) (err error) -//sys Listen(s int, backlog int) (err error) = libsocket.__xnet_llisten +//sys Listen(s int, backlog int) (err error) = libsocket.__xnet_listen //sys Lstat(path string, stat *Stat_t) (err error) //sys Madvise(b []byte, advice int) (err error) //sys Mkdir(path string, mode uint32) (err error) diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index b6db27d9..d0a75da5 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -853,20 +853,86 @@ const ( DM_VERSION_MAJOR = 0x4 DM_VERSION_MINOR = 0x32 DM_VERSION_PATCHLEVEL = 0x0 + DT_ADDRRNGHI = 0x6ffffeff + DT_ADDRRNGLO = 0x6ffffe00 DT_BLK = 0x6 DT_CHR = 0x2 + DT_DEBUG = 0x15 DT_DIR = 0x4 + DT_ENCODING = 0x20 DT_FIFO = 0x1 + DT_FINI = 0xd + DT_FLAGS_1 = 0x6ffffffb + DT_GNU_HASH = 0x6ffffef5 + DT_HASH = 0x4 + DT_HIOS = 0x6ffff000 + DT_HIPROC = 0x7fffffff + DT_INIT = 0xc + DT_JMPREL = 0x17 DT_LNK = 0xa + DT_LOOS = 0x6000000d + DT_LOPROC = 0x70000000 + DT_NEEDED = 0x1 + DT_NULL = 0x0 + DT_PLTGOT = 0x3 + DT_PLTREL = 0x14 + DT_PLTRELSZ = 0x2 DT_REG = 0x8 + DT_REL = 0x11 + DT_RELA = 0x7 + DT_RELACOUNT = 0x6ffffff9 + DT_RELAENT = 0x9 + DT_RELASZ = 0x8 + DT_RELCOUNT = 0x6ffffffa + DT_RELENT = 0x13 + DT_RELSZ = 0x12 + DT_RPATH = 0xf DT_SOCK = 0xc + DT_SONAME = 0xe + DT_STRSZ = 0xa + DT_STRTAB = 0x5 + DT_SYMBOLIC = 0x10 + DT_SYMENT = 0xb + DT_SYMTAB = 0x6 + DT_TEXTREL = 0x16 DT_UNKNOWN = 0x0 + DT_VALRNGHI = 0x6ffffdff + DT_VALRNGLO = 0x6ffffd00 + DT_VERDEF = 0x6ffffffc + DT_VERDEFNUM = 0x6ffffffd + DT_VERNEED = 0x6ffffffe + DT_VERNEEDNUM = 0x6fffffff + DT_VERSYM = 0x6ffffff0 DT_WHT = 0xe ECHO = 0x8 ECRYPTFS_SUPER_MAGIC = 0xf15f EFD_SEMAPHORE = 0x1 EFIVARFS_MAGIC = 0xde5e81e4 EFS_SUPER_MAGIC = 0x414a53 + EI_CLASS = 0x4 + EI_DATA = 0x5 + EI_MAG0 = 0x0 + EI_MAG1 = 0x1 + EI_MAG2 = 0x2 + EI_MAG3 = 0x3 + EI_NIDENT = 0x10 + EI_OSABI = 0x7 + EI_PAD = 0x8 + EI_VERSION = 0x6 + ELFCLASS32 = 0x1 + ELFCLASS64 = 0x2 + ELFCLASSNONE = 0x0 + ELFCLASSNUM = 0x3 + ELFDATA2LSB = 0x1 + ELFDATA2MSB = 0x2 + ELFDATANONE = 0x0 + ELFMAG = "\177ELF" + ELFMAG0 = 0x7f + ELFMAG1 = 'E' + ELFMAG2 = 'L' + ELFMAG3 = 'F' + ELFOSABI_LINUX = 0x3 + ELFOSABI_NONE = 0x0 EM_386 = 0x3 EM_486 = 0x6 EM_68K = 0x4 @@ -1152,14 +1218,24 @@ const ( ETH_P_WCCP = 0x883e ETH_P_X25 = 0x805 ETH_P_XDSA = 0xf8 + ET_CORE = 0x4 + ET_DYN = 0x3 + ET_EXEC = 0x2 + ET_HIPROC = 0xffff + ET_LOPROC = 0xff00 + ET_NONE = 0x0 + ET_REL = 0x1 EV_ABS = 0x3 EV_CNT = 0x20 + EV_CURRENT = 0x1 EV_FF = 0x15 EV_FF_STATUS = 0x17 EV_KEY = 0x1 EV_LED = 0x11 EV_MAX = 0x1f EV_MSC = 0x4 + EV_NONE = 0x0 + EV_NUM = 0x2 EV_PWR = 0x16 EV_REL = 0x2 EV_REP = 0x14 @@ -2276,7 +2352,167 @@ const ( NLM_F_REPLACE = 0x100 NLM_F_REQUEST = 0x1 NLM_F_ROOT = 0x100 + NN_386_IOPERM = "LINUX" + NN_386_TLS = "LINUX" + NN_ARC_V2 = "LINUX" + NN_ARM_FPMR = "LINUX" + NN_ARM_GCS = "LINUX" + NN_ARM_HW_BREAK = "LINUX" + NN_ARM_HW_WATCH = "LINUX" + NN_ARM_PACA_KEYS = "LINUX" + NN_ARM_PACG_KEYS = "LINUX" + NN_ARM_PAC_ENABLED_KEYS = "LINUX" + NN_ARM_PAC_MASK = "LINUX" + NN_ARM_POE = "LINUX" + NN_ARM_SSVE = "LINUX" + NN_ARM_SVE = "LINUX" + NN_ARM_SYSTEM_CALL = "LINUX" + NN_ARM_TAGGED_ADDR_CTRL = "LINUX" + NN_ARM_TLS = "LINUX" + NN_ARM_VFP = "LINUX" + NN_ARM_ZA = "LINUX" + NN_ARM_ZT = "LINUX" + NN_AUXV = "CORE" + NN_FILE = "CORE" + NN_GNU_PROPERTY_TYPE_0 = "GNU" + NN_LOONGARCH_CPUCFG = "LINUX" + NN_LOONGARCH_CSR = "LINUX" + NN_LOONGARCH_HW_BREAK = "LINUX" + NN_LOONGARCH_HW_WATCH = "LINUX" + NN_LOONGARCH_LASX = "LINUX" + NN_LOONGARCH_LBT = "LINUX" + NN_LOONGARCH_LSX = "LINUX" + NN_MIPS_DSP = "LINUX" + NN_MIPS_FP_MODE = "LINUX" + NN_MIPS_MSA = "LINUX" + NN_PPC_DEXCR = "LINUX" + NN_PPC_DSCR = "LINUX" + NN_PPC_EBB = "LINUX" + NN_PPC_HASHKEYR = "LINUX" + NN_PPC_PKEY = "LINUX" + NN_PPC_PMU = "LINUX" + NN_PPC_PPR = "LINUX" + NN_PPC_SPE = "LINUX" + NN_PPC_TAR = "LINUX" + NN_PPC_TM_CDSCR = "LINUX" + NN_PPC_TM_CFPR = "LINUX" + NN_PPC_TM_CGPR = "LINUX" + NN_PPC_TM_CPPR = "LINUX" + NN_PPC_TM_CTAR = "LINUX" + NN_PPC_TM_CVMX = "LINUX" + NN_PPC_TM_CVSX = "LINUX" + NN_PPC_TM_SPR = "LINUX" + NN_PPC_VMX = "LINUX" + NN_PPC_VSX = "LINUX" + NN_PRFPREG = "CORE" + NN_PRPSINFO = "CORE" + NN_PRSTATUS = "CORE" + NN_PRXFPREG = "LINUX" + NN_RISCV_CSR = "LINUX" + NN_RISCV_TAGGED_ADDR_CTRL = "LINUX" + NN_RISCV_VECTOR = "LINUX" + NN_S390_CTRS = "LINUX" + NN_S390_GS_BC = "LINUX" + NN_S390_GS_CB = "LINUX" + NN_S390_HIGH_GPRS = "LINUX" + NN_S390_LAST_BREAK = "LINUX" + NN_S390_PREFIX = "LINUX" + NN_S390_PV_CPU_DATA = "LINUX" + NN_S390_RI_CB = "LINUX" + NN_S390_SYSTEM_CALL = "LINUX" + NN_S390_TDB = "LINUX" + NN_S390_TIMER = "LINUX" + NN_S390_TODCMP = "LINUX" + NN_S390_TODPREG = "LINUX" + NN_S390_VXRS_HIGH = "LINUX" + NN_S390_VXRS_LOW = "LINUX" + NN_SIGINFO = "CORE" + NN_TASKSTRUCT = "CORE" + NN_VMCOREDD = "LINUX" + NN_X86_SHSTK = "LINUX" + NN_X86_XSAVE_LAYOUT = "LINUX" + NN_X86_XSTATE = "LINUX" NSFS_MAGIC = 0x6e736673 + NT_386_IOPERM = 0x201 + NT_386_TLS = 0x200 + NT_ARC_V2 = 0x600 + NT_ARM_FPMR = 0x40e + NT_ARM_GCS = 0x410 + NT_ARM_HW_BREAK = 0x402 + NT_ARM_HW_WATCH = 0x403 + NT_ARM_PACA_KEYS = 0x407 + NT_ARM_PACG_KEYS = 0x408 + NT_ARM_PAC_ENABLED_KEYS = 0x40a + NT_ARM_PAC_MASK = 0x406 + NT_ARM_POE = 0x40f + NT_ARM_SSVE = 0x40b + NT_ARM_SVE = 0x405 + NT_ARM_SYSTEM_CALL = 0x404 + NT_ARM_TAGGED_ADDR_CTRL = 0x409 + NT_ARM_TLS = 0x401 + NT_ARM_VFP = 0x400 + NT_ARM_ZA = 0x40c + NT_ARM_ZT = 0x40d + NT_AUXV = 0x6 + NT_FILE = 0x46494c45 + NT_GNU_PROPERTY_TYPE_0 = 0x5 + NT_LOONGARCH_CPUCFG = 0xa00 + NT_LOONGARCH_CSR = 0xa01 + NT_LOONGARCH_HW_BREAK = 0xa05 + NT_LOONGARCH_HW_WATCH = 0xa06 + NT_LOONGARCH_LASX = 0xa03 + NT_LOONGARCH_LBT = 0xa04 + NT_LOONGARCH_LSX = 0xa02 + NT_MIPS_DSP = 0x800 + NT_MIPS_FP_MODE = 0x801 + NT_MIPS_MSA = 0x802 + NT_PPC_DEXCR = 0x111 + NT_PPC_DSCR = 0x105 + NT_PPC_EBB = 0x106 + NT_PPC_HASHKEYR = 0x112 + NT_PPC_PKEY = 0x110 + NT_PPC_PMU = 0x107 + NT_PPC_PPR = 0x104 + NT_PPC_SPE = 0x101 + NT_PPC_TAR = 0x103 + NT_PPC_TM_CDSCR = 0x10f + NT_PPC_TM_CFPR = 0x109 + NT_PPC_TM_CGPR = 0x108 + NT_PPC_TM_CPPR = 0x10e + NT_PPC_TM_CTAR = 0x10d + NT_PPC_TM_CVMX = 0x10a + NT_PPC_TM_CVSX = 0x10b + NT_PPC_TM_SPR = 0x10c + NT_PPC_VMX = 0x100 + NT_PPC_VSX = 0x102 + NT_PRFPREG = 0x2 + NT_PRPSINFO = 0x3 + NT_PRSTATUS = 0x1 + NT_PRXFPREG = 0x46e62b7f + NT_RISCV_CSR = 0x900 + NT_RISCV_TAGGED_ADDR_CTRL = 0x902 + NT_RISCV_VECTOR = 0x901 + NT_S390_CTRS = 0x304 + NT_S390_GS_BC = 0x30c + NT_S390_GS_CB = 0x30b + NT_S390_HIGH_GPRS = 0x300 + NT_S390_LAST_BREAK = 0x306 + NT_S390_PREFIX = 0x305 + NT_S390_PV_CPU_DATA = 0x30e + NT_S390_RI_CB = 0x30d + NT_S390_SYSTEM_CALL = 0x307 + NT_S390_TDB = 0x308 + NT_S390_TIMER = 0x301 + NT_S390_TODCMP = 0x302 + NT_S390_TODPREG = 0x303 + NT_S390_VXRS_HIGH = 0x30a + NT_S390_VXRS_LOW = 0x309 + NT_SIGINFO = 0x53494749 + NT_TASKSTRUCT = 0x4 + NT_VMCOREDD = 0x700 + NT_X86_SHSTK = 0x204 + NT_X86_XSAVE_LAYOUT = 0x205 + NT_X86_XSTATE = 0x202 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 @@ -2463,6 +2699,59 @@ const ( PERF_RECORD_MISC_USER = 0x2 PERF_SAMPLE_BRANCH_PLM_ALL = 0x7 PERF_SAMPLE_WEIGHT_TYPE = 0x1004000 + PF_ALG = 0x26 + PF_APPLETALK = 0x5 + PF_ASH = 0x12 + PF_ATMPVC = 0x8 + PF_ATMSVC = 0x14 + PF_AX25 = 0x3 + PF_BLUETOOTH = 0x1f + PF_BRIDGE = 0x7 + PF_CAIF = 0x25 + PF_CAN = 0x1d + PF_DECnet = 0xc + PF_ECONET = 0x13 + PF_FILE = 0x1 + PF_IB = 0x1b + PF_IEEE802154 = 0x24 + PF_INET = 0x2 + PF_INET6 = 0xa + PF_IPX = 0x4 + PF_IRDA = 0x17 + PF_ISDN = 0x22 + PF_IUCV = 0x20 + PF_KCM = 0x29 + PF_KEY = 0xf + PF_LLC = 0x1a + PF_LOCAL = 0x1 + PF_MAX = 0x2e + PF_MCTP = 0x2d + PF_MPLS = 0x1c + PF_NETBEUI = 0xd + PF_NETLINK = 0x10 + PF_NETROM = 0x6 + PF_NFC = 0x27 + PF_PACKET = 0x11 + PF_PHONET = 0x23 + PF_PPPOX = 0x18 + PF_QIPCRTR = 0x2a + PF_R = 0x4 + PF_RDS = 0x15 + PF_ROSE = 0xb + PF_ROUTE = 0x10 + PF_RXRPC = 0x21 + PF_SECURITY = 0xe + PF_SMC = 0x2b + PF_SNA = 0x16 + PF_TIPC = 0x1e + PF_UNIX = 0x1 + PF_UNSPEC = 0x0 + PF_VSOCK = 0x28 + PF_W = 0x2 + PF_WANPIPE = 0x19 + PF_X = 0x1 + PF_X25 = 0x9 + PF_XDP = 0x2c PID_FS_MAGIC = 0x50494446 PIPEFS_MAGIC = 0x50495045 PPPIOCGNPMODE = 0xc008744c @@ -2758,6 +3047,23 @@ const ( PTRACE_SYSCALL_INFO_NONE = 0x0 PTRACE_SYSCALL_INFO_SECCOMP = 0x3 PTRACE_TRACEME = 0x0 + PT_AARCH64_MEMTAG_MTE = 0x70000002 + PT_DYNAMIC = 0x2 + PT_GNU_EH_FRAME = 0x6474e550 + PT_GNU_PROPERTY = 0x6474e553 + PT_GNU_RELRO = 0x6474e552 + PT_GNU_STACK = 0x6474e551 + PT_HIOS = 0x6fffffff + PT_HIPROC = 0x7fffffff + PT_INTERP = 0x3 + PT_LOAD = 0x1 + PT_LOOS = 0x60000000 + PT_LOPROC = 0x70000000 + PT_NOTE = 0x4 + PT_NULL = 0x0 + PT_PHDR = 0x6 + PT_SHLIB = 0x5 + PT_TLS = 0x7 P_ALL = 0x0 P_PGID = 0x2 P_PID = 0x1 @@ -3091,6 +3397,47 @@ const ( SEEK_MAX = 0x4 SEEK_SET = 0x0 SELINUX_MAGIC = 0xf97cff8c + SHF_ALLOC = 0x2 + SHF_EXCLUDE = 0x8000000 + SHF_EXECINSTR = 0x4 + SHF_GROUP = 0x200 + SHF_INFO_LINK = 0x40 + SHF_LINK_ORDER = 0x80 + SHF_MASKOS = 0xff00000 + SHF_MASKPROC = 0xf0000000 + SHF_MERGE = 0x10 + SHF_ORDERED = 0x4000000 + SHF_OS_NONCONFORMING = 0x100 + SHF_RELA_LIVEPATCH = 0x100000 + SHF_RO_AFTER_INIT = 0x200000 + SHF_STRINGS = 0x20 + SHF_TLS = 0x400 + SHF_WRITE = 0x1 + SHN_ABS = 0xfff1 + SHN_COMMON = 0xfff2 + SHN_HIPROC = 0xff1f + SHN_HIRESERVE = 0xffff + SHN_LIVEPATCH = 0xff20 + SHN_LOPROC = 0xff00 + SHN_LORESERVE = 0xff00 + SHN_UNDEF = 0x0 + SHT_DYNAMIC = 0x6 + SHT_DYNSYM = 0xb + SHT_HASH = 0x5 + SHT_HIPROC = 0x7fffffff + SHT_HIUSER = 0xffffffff + SHT_LOPROC = 0x70000000 + SHT_LOUSER = 0x80000000 + SHT_NOBITS = 0x8 + SHT_NOTE = 0x7 + SHT_NULL = 0x0 + SHT_NUM = 0xc + SHT_PROGBITS = 0x1 + SHT_REL = 0x9 + SHT_RELA = 0x4 + SHT_SHLIB = 0xa + SHT_STRTAB = 0x3 + SHT_SYMTAB = 0x2 SHUT_RD = 0x0 SHUT_RDWR = 0x2 SHUT_WR = 0x1 @@ -3317,6 +3664,16 @@ const ( STATX_UID = 0x8 STATX_WRITE_ATOMIC = 0x10000 STATX__RESERVED = 0x80000000 + STB_GLOBAL = 0x1 + STB_LOCAL = 0x0 + STB_WEAK = 0x2 + STT_COMMON = 0x5 + STT_FILE = 0x4 + STT_FUNC = 0x2 + STT_NOTYPE = 0x0 + STT_OBJECT = 0x1 + STT_SECTION = 0x3 + STT_TLS = 0x6 SYNC_FILE_RANGE_WAIT_AFTER = 0x4 SYNC_FILE_RANGE_WAIT_BEFORE = 0x1 SYNC_FILE_RANGE_WRITE = 0x2 @@ -3553,6 +3910,8 @@ const ( UTIME_OMIT = 0x3ffffffe V9FS_MAGIC = 0x1021997 VERASE = 0x2 + VER_FLG_BASE = 0x1 + VER_FLG_WEAK = 0x2 VINTR = 0x0 VKILL = 0x3 VLNEXT = 0xf diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 5cc1e8eb..8935d10a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -2238,3 +2238,13 @@ func Mseal(b []byte, flags uint) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setMemPolicy(mode int, mask *CPUSet, size int) (err error) { + _, _, e1 := Syscall(SYS_SET_MEMPOLICY, uintptr(mode), uintptr(unsafe.Pointer(mask)), uintptr(size)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index c6545413..b4609c20 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -72,7 +72,7 @@ import ( //go:cgo_import_dynamic libc_kill kill "libc.so" //go:cgo_import_dynamic libc_lchown lchown "libc.so" //go:cgo_import_dynamic libc_link link "libc.so" -//go:cgo_import_dynamic libc___xnet_llisten __xnet_llisten "libsocket.so" +//go:cgo_import_dynamic libc___xnet_listen __xnet_listen "libsocket.so" //go:cgo_import_dynamic libc_lstat lstat "libc.so" //go:cgo_import_dynamic libc_madvise madvise "libc.so" //go:cgo_import_dynamic libc_mkdir mkdir "libc.so" @@ -221,7 +221,7 @@ import ( //go:linkname procKill libc_kill //go:linkname procLchown libc_lchown //go:linkname procLink libc_link -//go:linkname proc__xnet_llisten libc___xnet_llisten +//go:linkname proc__xnet_listen libc___xnet_listen //go:linkname procLstat libc_lstat //go:linkname procMadvise libc_madvise //go:linkname procMkdir libc_mkdir @@ -371,7 +371,7 @@ var ( procKill, procLchown, procLink, - proc__xnet_llisten, + proc__xnet_listen, procLstat, procMadvise, procMkdir, @@ -1178,7 +1178,7 @@ func Link(path string, link string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Listen(s int, backlog int) (err error) { - _, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&proc__xnet_llisten)), 2, uintptr(s), uintptr(backlog), 0, 0, 0, 0) + _, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&proc__xnet_listen)), 2, uintptr(s), uintptr(backlog), 0, 0, 0, 0) if e1 != 0 { err = errnoErr(e1) } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index cd236443..c1a46701 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -632,6 +632,8 @@ const ( IFA_FLAGS = 0x8 IFA_RT_PRIORITY = 0x9 IFA_TARGET_NETNSID = 0xa + IFAL_LABEL = 0x2 + IFAL_ADDRESS = 0x1 RT_SCOPE_UNIVERSE = 0x0 RT_SCOPE_SITE = 0xc8 RT_SCOPE_LINK = 0xfd @@ -689,6 +691,7 @@ const ( SizeofRtAttr = 0x4 SizeofIfInfomsg = 0x10 SizeofIfAddrmsg = 0x8 + SizeofIfAddrlblmsg = 0xc SizeofIfaCacheinfo = 0x10 SizeofRtMsg = 0xc SizeofRtNexthop = 0x8 @@ -740,6 +743,15 @@ type IfAddrmsg struct { Index uint32 } +type IfAddrlblmsg struct { + Family uint8 + _ uint8 + Prefixlen uint8 + Flags uint8 + Index uint32 + Seq uint32 +} + type IfaCacheinfo struct { Prefered uint32 Valid uint32 @@ -3052,6 +3064,23 @@ const ( ) const ( + TCA_UNSPEC = 0x0 + TCA_KIND = 0x1 + TCA_OPTIONS = 0x2 + TCA_STATS = 0x3 + TCA_XSTATS = 0x4 + TCA_RATE = 0x5 + TCA_FCNT = 0x6 + TCA_STATS2 = 0x7 + TCA_STAB = 0x8 + TCA_PAD = 0x9 + TCA_DUMP_INVISIBLE = 0xa + TCA_CHAIN = 0xb + TCA_HW_OFFLOAD = 0xc + TCA_INGRESS_BLOCK = 0xd + TCA_EGRESS_BLOCK = 0xe + TCA_DUMP_FLAGS = 0xf + TCA_EXT_WARN_MSG = 0x10 RTNLGRP_NONE = 0x0 RTNLGRP_LINK = 0x1 RTNLGRP_NOTIFY = 0x2 @@ -3086,6 +3115,18 @@ const ( RTNLGRP_IPV6_MROUTE_R = 0x1f RTNLGRP_NEXTHOP = 0x20 RTNLGRP_BRVLAN = 0x21 + RTNLGRP_MCTP_IFADDR = 0x22 + RTNLGRP_TUNNEL = 0x23 + RTNLGRP_STATS = 0x24 + RTNLGRP_IPV4_MCADDR = 0x25 + RTNLGRP_IPV6_MCADDR = 0x26 + RTNLGRP_IPV6_ACADDR = 0x27 + TCA_ROOT_UNSPEC = 0x0 + TCA_ROOT_TAB = 0x1 + TCA_ROOT_FLAGS = 0x2 + TCA_ROOT_COUNT = 0x3 + TCA_ROOT_TIME_DELTA = 0x4 + TCA_ROOT_EXT_WARN_MSG = 0x5 ) type CapUserHeader struct { @@ -3549,6 +3590,8 @@ type Nhmsg struct { Flags uint32 } +const SizeofNhmsg = 0x8 + type NexthopGrp struct { Id uint32 Weight uint8 @@ -3556,6 +3599,8 @@ type NexthopGrp struct { Resvd2 uint16 } +const SizeofNexthopGrp = 0x8 + const ( NHA_UNSPEC = 0x0 NHA_ID = 0x1 @@ -6291,3 +6336,30 @@ type SockDiagReq struct { } const RTM_NEWNVLAN = 0x70 + +const ( + MPOL_BIND = 0x2 + MPOL_DEFAULT = 0x0 + MPOL_F_ADDR = 0x2 + MPOL_F_MEMS_ALLOWED = 0x4 + MPOL_F_MOF = 0x8 + MPOL_F_MORON = 0x10 + MPOL_F_NODE = 0x1 + MPOL_F_NUMA_BALANCING = 0x2000 + MPOL_F_RELATIVE_NODES = 0x4000 + MPOL_F_SHARED = 0x1 + MPOL_F_STATIC_NODES = 0x8000 + MPOL_INTERLEAVE = 0x3 + MPOL_LOCAL = 0x4 + MPOL_MAX = 0x7 + MPOL_MF_INTERNAL = 0x10 + MPOL_MF_LAZY = 0x8 + MPOL_MF_MOVE_ALL = 0x4 + MPOL_MF_MOVE = 0x2 + MPOL_MF_STRICT = 0x1 + MPOL_MF_VALID = 0x7 + MPOL_MODE_FLAGS = 0xe000 + MPOL_PREFERRED = 0x1 + MPOL_PREFERRED_MANY = 0x5 + MPOL_WEIGHTED_INTERLEAVE = 0x6 +) diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 640f6b15..69439df2 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -321,6 +321,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetConsoleOutputCP(cp uint32) (err error) = kernel32.SetConsoleOutputCP //sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW //sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW +//sys GetNumberOfConsoleInputEvents(console Handle, numevents *uint32) (err error) = kernel32.GetNumberOfConsoleInputEvents +//sys FlushConsoleInputBuffer(console Handle) (err error) = kernel32.FlushConsoleInputBuffer //sys resizePseudoConsole(pconsole Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole //sys CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot //sys Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW @@ -890,8 +892,12 @@ const socket_error = uintptr(^uint32(0)) //sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar //sys getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) = iphlpapi.GetBestInterfaceEx //sys GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) = iphlpapi.GetIfEntry2Ex +//sys GetIpForwardEntry2(row *MibIpForwardRow2) (errcode error) = iphlpapi.GetIpForwardEntry2 +//sys GetIpForwardTable2(family uint16, table **MibIpForwardTable2) (errcode error) = iphlpapi.GetIpForwardTable2 //sys GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) = iphlpapi.GetUnicastIpAddressEntry +//sys FreeMibTable(memory unsafe.Pointer) = iphlpapi.FreeMibTable //sys NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyIpInterfaceChange +//sys NotifyRouteChange2(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyRouteChange2 //sys NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyUnicastIpAddressChange //sys CancelMibChangeNotify2(notificationHandle Handle) (errcode error) = iphlpapi.CancelMibChangeNotify2 @@ -914,6 +920,17 @@ type RawSockaddrInet6 struct { Scope_id uint32 } +// RawSockaddrInet is a union that contains an IPv4, an IPv6 address, or an address family. See +// https://learn.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_inet. +// +// A [*RawSockaddrInet] may be converted to a [*RawSockaddrInet4] or [*RawSockaddrInet6] using +// unsafe, depending on the address family. +type RawSockaddrInet struct { + Family uint16 + Port uint16 + Data [6]uint32 +} + type RawSockaddr struct { Family uint16 Data [14]int8 diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 958bcf47..6e4f50eb 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -65,6 +65,22 @@ var signals = [...]string{ 15: "terminated", } +// File flags for [os.OpenFile]. The O_ prefix is used to indicate +// that these flags are specific to the OpenFile function. +const ( + O_FILE_FLAG_OPEN_NO_RECALL = FILE_FLAG_OPEN_NO_RECALL + O_FILE_FLAG_OPEN_REPARSE_POINT = FILE_FLAG_OPEN_REPARSE_POINT + O_FILE_FLAG_SESSION_AWARE = FILE_FLAG_SESSION_AWARE + O_FILE_FLAG_POSIX_SEMANTICS = FILE_FLAG_POSIX_SEMANTICS + O_FILE_FLAG_BACKUP_SEMANTICS = FILE_FLAG_BACKUP_SEMANTICS + O_FILE_FLAG_DELETE_ON_CLOSE = FILE_FLAG_DELETE_ON_CLOSE + O_FILE_FLAG_SEQUENTIAL_SCAN = FILE_FLAG_SEQUENTIAL_SCAN + O_FILE_FLAG_RANDOM_ACCESS = FILE_FLAG_RANDOM_ACCESS + O_FILE_FLAG_NO_BUFFERING = FILE_FLAG_NO_BUFFERING + O_FILE_FLAG_OVERLAPPED = FILE_FLAG_OVERLAPPED + O_FILE_FLAG_WRITE_THROUGH = FILE_FLAG_WRITE_THROUGH +) + const ( FILE_READ_DATA = 0x00000001 FILE_READ_ATTRIBUTES = 0x00000080 @@ -1976,6 +1992,12 @@ const ( SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 ) +// FILE_ZERO_DATA_INFORMATION from winioctl.h +type FileZeroDataInformation struct { + FileOffset int64 + BeyondFinalZero int64 +} + const ( ComputerNameNetBIOS = 0 ComputerNameDnsHostname = 1 @@ -2298,6 +2320,82 @@ type MibIfRow2 struct { OutQLen uint64 } +// IP_ADDRESS_PREFIX stores an IP address prefix. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-ip_address_prefix. +type IpAddressPrefix struct { + Prefix RawSockaddrInet + PrefixLength uint8 +} + +// NL_ROUTE_ORIGIN enumeration from nldef.h or +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_route_origin. +const ( + NlroManual = 0 + NlroWellKnown = 1 + NlroDHCP = 2 + NlroRouterAdvertisement = 3 + Nlro6to4 = 4 +) + +// NL_ROUTE_ORIGIN enumeration from nldef.h or +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_route_protocol. +const ( + MIB_IPPROTO_OTHER = 1 + MIB_IPPROTO_LOCAL = 2 + MIB_IPPROTO_NETMGMT = 3 + MIB_IPPROTO_ICMP = 4 + MIB_IPPROTO_EGP = 5 + MIB_IPPROTO_GGP = 6 + MIB_IPPROTO_HELLO = 7 + MIB_IPPROTO_RIP = 8 + MIB_IPPROTO_IS_IS = 9 + MIB_IPPROTO_ES_IS = 10 + MIB_IPPROTO_CISCO = 11 + MIB_IPPROTO_BBN = 12 + MIB_IPPROTO_OSPF = 13 + MIB_IPPROTO_BGP = 14 + MIB_IPPROTO_IDPR = 15 + MIB_IPPROTO_EIGRP = 16 + MIB_IPPROTO_DVMRP = 17 + MIB_IPPROTO_RPL = 18 + MIB_IPPROTO_DHCP = 19 + MIB_IPPROTO_NT_AUTOSTATIC = 10002 + MIB_IPPROTO_NT_STATIC = 10006 + MIB_IPPROTO_NT_STATIC_NON_DOD = 10007 +) + +// MIB_IPFORWARD_ROW2 stores information about an IP route entry. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipforward_row2. +type MibIpForwardRow2 struct { + InterfaceLuid uint64 + InterfaceIndex uint32 + DestinationPrefix IpAddressPrefix + NextHop RawSockaddrInet + SitePrefixLength uint8 + ValidLifetime uint32 + PreferredLifetime uint32 + Metric uint32 + Protocol uint32 + Loopback uint8 + AutoconfigureAddress uint8 + Publish uint8 + Immortal uint8 + Age uint32 + Origin uint32 +} + +// MIB_IPFORWARD_TABLE2 contains a table of IP route entries. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipforward_table2. +type MibIpForwardTable2 struct { + NumEntries uint32 + Table [1]MibIpForwardRow2 +} + +// Rows returns the IP route entries in the table. +func (t *MibIpForwardTable2) Rows() []MibIpForwardRow2 { + return unsafe.Slice(&t.Table[0], t.NumEntries) +} + // MIB_UNICASTIPADDRESS_ROW stores information about a unicast IP address. See // https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_row. type MibUnicastIpAddressRow struct { diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index a58bc48b..f25b7308 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -182,13 +182,17 @@ var ( procDwmGetWindowAttribute = moddwmapi.NewProc("DwmGetWindowAttribute") procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute") procCancelMibChangeNotify2 = modiphlpapi.NewProc("CancelMibChangeNotify2") + procFreeMibTable = modiphlpapi.NewProc("FreeMibTable") procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex") + procGetIpForwardEntry2 = modiphlpapi.NewProc("GetIpForwardEntry2") + procGetIpForwardTable2 = modiphlpapi.NewProc("GetIpForwardTable2") procGetUnicastIpAddressEntry = modiphlpapi.NewProc("GetUnicastIpAddressEntry") procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange") + procNotifyRouteChange2 = modiphlpapi.NewProc("NotifyRouteChange2") procNotifyUnicastIpAddressChange = modiphlpapi.NewProc("NotifyUnicastIpAddressChange") procAddDllDirectory = modkernel32.NewProc("AddDllDirectory") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") @@ -238,6 +242,7 @@ var ( procFindResourceW = modkernel32.NewProc("FindResourceW") procFindVolumeClose = modkernel32.NewProc("FindVolumeClose") procFindVolumeMountPointClose = modkernel32.NewProc("FindVolumeMountPointClose") + procFlushConsoleInputBuffer = modkernel32.NewProc("FlushConsoleInputBuffer") procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile") procFormatMessageW = modkernel32.NewProc("FormatMessageW") @@ -284,6 +289,7 @@ var ( procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") procGetNamedPipeServerProcessId = modkernel32.NewProc("GetNamedPipeServerProcessId") + procGetNumberOfConsoleInputEvents = modkernel32.NewProc("GetNumberOfConsoleInputEvents") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procGetPriorityClass = modkernel32.NewProc("GetPriorityClass") procGetProcAddress = modkernel32.NewProc("GetProcAddress") @@ -546,25 +552,25 @@ var ( ) func cm_Get_DevNode_Status(status *uint32, problemNumber *uint32, devInst DEVINST, flags uint32) (ret CONFIGRET) { - r0, _, _ := syscall.Syscall6(procCM_Get_DevNode_Status.Addr(), 4, uintptr(unsafe.Pointer(status)), uintptr(unsafe.Pointer(problemNumber)), uintptr(devInst), uintptr(flags), 0, 0) + r0, _, _ := syscall.SyscallN(procCM_Get_DevNode_Status.Addr(), uintptr(unsafe.Pointer(status)), uintptr(unsafe.Pointer(problemNumber)), uintptr(devInst), uintptr(flags)) ret = CONFIGRET(r0) return } func cm_Get_Device_Interface_List(interfaceClass *GUID, deviceID *uint16, buffer *uint16, bufferLen uint32, flags uint32) (ret CONFIGRET) { - r0, _, _ := syscall.Syscall6(procCM_Get_Device_Interface_ListW.Addr(), 5, uintptr(unsafe.Pointer(interfaceClass)), uintptr(unsafe.Pointer(deviceID)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferLen), uintptr(flags), 0) + r0, _, _ := syscall.SyscallN(procCM_Get_Device_Interface_ListW.Addr(), uintptr(unsafe.Pointer(interfaceClass)), uintptr(unsafe.Pointer(deviceID)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferLen), uintptr(flags)) ret = CONFIGRET(r0) return } func cm_Get_Device_Interface_List_Size(len *uint32, interfaceClass *GUID, deviceID *uint16, flags uint32) (ret CONFIGRET) { - r0, _, _ := syscall.Syscall6(procCM_Get_Device_Interface_List_SizeW.Addr(), 4, uintptr(unsafe.Pointer(len)), uintptr(unsafe.Pointer(interfaceClass)), uintptr(unsafe.Pointer(deviceID)), uintptr(flags), 0, 0) + r0, _, _ := syscall.SyscallN(procCM_Get_Device_Interface_List_SizeW.Addr(), uintptr(unsafe.Pointer(len)), uintptr(unsafe.Pointer(interfaceClass)), uintptr(unsafe.Pointer(deviceID)), uintptr(flags)) ret = CONFIGRET(r0) return } func cm_MapCrToWin32Err(configRet CONFIGRET, defaultWin32Error Errno) (ret Errno) { - r0, _, _ := syscall.Syscall(procCM_MapCrToWin32Err.Addr(), 2, uintptr(configRet), uintptr(defaultWin32Error), 0) + r0, _, _ := syscall.SyscallN(procCM_MapCrToWin32Err.Addr(), uintptr(configRet), uintptr(defaultWin32Error)) ret = Errno(r0) return } @@ -574,7 +580,7 @@ func AdjustTokenGroups(token Token, resetToDefault bool, newstate *Tokengroups, if resetToDefault { _p0 = 1 } - r1, _, e1 := syscall.Syscall6(procAdjustTokenGroups.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) + r1, _, e1 := syscall.SyscallN(procAdjustTokenGroups.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) if r1 == 0 { err = errnoErr(e1) } @@ -586,7 +592,7 @@ func AdjustTokenPrivileges(token Token, disableAllPrivileges bool, newstate *Tok if disableAllPrivileges { _p0 = 1 } - r1, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) + r1, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) if r1 == 0 { err = errnoErr(e1) } @@ -594,7 +600,7 @@ func AdjustTokenPrivileges(token Token, disableAllPrivileges bool, newstate *Tok } func AllocateAndInitializeSid(identAuth *SidIdentifierAuthority, subAuth byte, subAuth0 uint32, subAuth1 uint32, subAuth2 uint32, subAuth3 uint32, subAuth4 uint32, subAuth5 uint32, subAuth6 uint32, subAuth7 uint32, sid **SID) (err error) { - r1, _, e1 := syscall.Syscall12(procAllocateAndInitializeSid.Addr(), 11, uintptr(unsafe.Pointer(identAuth)), uintptr(subAuth), uintptr(subAuth0), uintptr(subAuth1), uintptr(subAuth2), uintptr(subAuth3), uintptr(subAuth4), uintptr(subAuth5), uintptr(subAuth6), uintptr(subAuth7), uintptr(unsafe.Pointer(sid)), 0) + r1, _, e1 := syscall.SyscallN(procAllocateAndInitializeSid.Addr(), uintptr(unsafe.Pointer(identAuth)), uintptr(subAuth), uintptr(subAuth0), uintptr(subAuth1), uintptr(subAuth2), uintptr(subAuth3), uintptr(subAuth4), uintptr(subAuth5), uintptr(subAuth6), uintptr(subAuth7), uintptr(unsafe.Pointer(sid))) if r1 == 0 { err = errnoErr(e1) } @@ -602,7 +608,7 @@ func AllocateAndInitializeSid(identAuth *SidIdentifierAuthority, subAuth byte, s } func buildSecurityDescriptor(owner *TRUSTEE, group *TRUSTEE, countAccessEntries uint32, accessEntries *EXPLICIT_ACCESS, countAuditEntries uint32, auditEntries *EXPLICIT_ACCESS, oldSecurityDescriptor *SECURITY_DESCRIPTOR, sizeNewSecurityDescriptor *uint32, newSecurityDescriptor **SECURITY_DESCRIPTOR) (ret error) { - r0, _, _ := syscall.Syscall9(procBuildSecurityDescriptorW.Addr(), 9, uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(countAccessEntries), uintptr(unsafe.Pointer(accessEntries)), uintptr(countAuditEntries), uintptr(unsafe.Pointer(auditEntries)), uintptr(unsafe.Pointer(oldSecurityDescriptor)), uintptr(unsafe.Pointer(sizeNewSecurityDescriptor)), uintptr(unsafe.Pointer(newSecurityDescriptor))) + r0, _, _ := syscall.SyscallN(procBuildSecurityDescriptorW.Addr(), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(countAccessEntries), uintptr(unsafe.Pointer(accessEntries)), uintptr(countAuditEntries), uintptr(unsafe.Pointer(auditEntries)), uintptr(unsafe.Pointer(oldSecurityDescriptor)), uintptr(unsafe.Pointer(sizeNewSecurityDescriptor)), uintptr(unsafe.Pointer(newSecurityDescriptor))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -610,7 +616,7 @@ func buildSecurityDescriptor(owner *TRUSTEE, group *TRUSTEE, countAccessEntries } func ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err error) { - r1, _, e1 := syscall.Syscall(procChangeServiceConfig2W.Addr(), 3, uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(info))) + r1, _, e1 := syscall.SyscallN(procChangeServiceConfig2W.Addr(), uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(info))) if r1 == 0 { err = errnoErr(e1) } @@ -618,7 +624,7 @@ func ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err err } func ChangeServiceConfig(service Handle, serviceType uint32, startType uint32, errorControl uint32, binaryPathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16, displayName *uint16) (err error) { - r1, _, e1 := syscall.Syscall12(procChangeServiceConfigW.Addr(), 11, uintptr(service), uintptr(serviceType), uintptr(startType), uintptr(errorControl), uintptr(unsafe.Pointer(binaryPathName)), uintptr(unsafe.Pointer(loadOrderGroup)), uintptr(unsafe.Pointer(tagId)), uintptr(unsafe.Pointer(dependencies)), uintptr(unsafe.Pointer(serviceStartName)), uintptr(unsafe.Pointer(password)), uintptr(unsafe.Pointer(displayName)), 0) + r1, _, e1 := syscall.SyscallN(procChangeServiceConfigW.Addr(), uintptr(service), uintptr(serviceType), uintptr(startType), uintptr(errorControl), uintptr(unsafe.Pointer(binaryPathName)), uintptr(unsafe.Pointer(loadOrderGroup)), uintptr(unsafe.Pointer(tagId)), uintptr(unsafe.Pointer(dependencies)), uintptr(unsafe.Pointer(serviceStartName)), uintptr(unsafe.Pointer(password)), uintptr(unsafe.Pointer(displayName))) if r1 == 0 { err = errnoErr(e1) } @@ -626,7 +632,7 @@ func ChangeServiceConfig(service Handle, serviceType uint32, startType uint32, e } func checkTokenMembership(tokenHandle Token, sidToCheck *SID, isMember *int32) (err error) { - r1, _, e1 := syscall.Syscall(procCheckTokenMembership.Addr(), 3, uintptr(tokenHandle), uintptr(unsafe.Pointer(sidToCheck)), uintptr(unsafe.Pointer(isMember))) + r1, _, e1 := syscall.SyscallN(procCheckTokenMembership.Addr(), uintptr(tokenHandle), uintptr(unsafe.Pointer(sidToCheck)), uintptr(unsafe.Pointer(isMember))) if r1 == 0 { err = errnoErr(e1) } @@ -634,7 +640,7 @@ func checkTokenMembership(tokenHandle Token, sidToCheck *SID, isMember *int32) ( } func CloseServiceHandle(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procCloseServiceHandle.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procCloseServiceHandle.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -642,7 +648,7 @@ func CloseServiceHandle(handle Handle) (err error) { } func ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) { - r1, _, e1 := syscall.Syscall(procControlService.Addr(), 3, uintptr(service), uintptr(control), uintptr(unsafe.Pointer(status))) + r1, _, e1 := syscall.SyscallN(procControlService.Addr(), uintptr(service), uintptr(control), uintptr(unsafe.Pointer(status))) if r1 == 0 { err = errnoErr(e1) } @@ -650,7 +656,7 @@ func ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err } func convertSecurityDescriptorToStringSecurityDescriptor(sd *SECURITY_DESCRIPTOR, revision uint32, securityInformation SECURITY_INFORMATION, str **uint16, strLen *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(securityInformation), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(strLen)), 0) + r1, _, e1 := syscall.SyscallN(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(securityInformation), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(strLen))) if r1 == 0 { err = errnoErr(e1) } @@ -658,7 +664,7 @@ func convertSecurityDescriptorToStringSecurityDescriptor(sd *SECURITY_DESCRIPTOR } func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) { - r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0) + r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid))) if r1 == 0 { err = errnoErr(e1) } @@ -675,7 +681,7 @@ func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision ui } func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd **SECURITY_DESCRIPTOR, size *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) + r1, _, e1 := syscall.SyscallN(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size))) if r1 == 0 { err = errnoErr(e1) } @@ -683,7 +689,7 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision } func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) { - r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0) + r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid))) if r1 == 0 { err = errnoErr(e1) } @@ -691,7 +697,7 @@ func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) { } func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) { - r1, _, e1 := syscall.Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid))) + r1, _, e1 := syscall.SyscallN(procCopySid.Addr(), uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid))) if r1 == 0 { err = errnoErr(e1) } @@ -703,7 +709,7 @@ func CreateProcessAsUser(token Token, appName *uint16, commandLine *uint16, proc if inheritHandles { _p0 = 1 } - r1, _, e1 := syscall.Syscall12(procCreateProcessAsUserW.Addr(), 11, uintptr(token), uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0) + r1, _, e1 := syscall.SyscallN(procCreateProcessAsUserW.Addr(), uintptr(token), uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo))) if r1 == 0 { err = errnoErr(e1) } @@ -711,7 +717,7 @@ func CreateProcessAsUser(token Token, appName *uint16, commandLine *uint16, proc } func CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall15(procCreateServiceW.Addr(), 13, uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(unsafe.Pointer(displayName)), uintptr(access), uintptr(srvType), uintptr(startType), uintptr(errCtl), uintptr(unsafe.Pointer(pathName)), uintptr(unsafe.Pointer(loadOrderGroup)), uintptr(unsafe.Pointer(tagId)), uintptr(unsafe.Pointer(dependencies)), uintptr(unsafe.Pointer(serviceStartName)), uintptr(unsafe.Pointer(password)), 0, 0) + r0, _, e1 := syscall.SyscallN(procCreateServiceW.Addr(), uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(unsafe.Pointer(displayName)), uintptr(access), uintptr(srvType), uintptr(startType), uintptr(errCtl), uintptr(unsafe.Pointer(pathName)), uintptr(unsafe.Pointer(loadOrderGroup)), uintptr(unsafe.Pointer(tagId)), uintptr(unsafe.Pointer(dependencies)), uintptr(unsafe.Pointer(serviceStartName)), uintptr(unsafe.Pointer(password))) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -720,7 +726,7 @@ func CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access } func createWellKnownSid(sidType WELL_KNOWN_SID_TYPE, domainSid *SID, sid *SID, sizeSid *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procCreateWellKnownSid.Addr(), 4, uintptr(sidType), uintptr(unsafe.Pointer(domainSid)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sizeSid)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCreateWellKnownSid.Addr(), uintptr(sidType), uintptr(unsafe.Pointer(domainSid)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sizeSid))) if r1 == 0 { err = errnoErr(e1) } @@ -728,7 +734,7 @@ func createWellKnownSid(sidType WELL_KNOWN_SID_TYPE, domainSid *SID, sid *SID, s } func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procCryptAcquireContextW.Addr(), uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -736,7 +742,7 @@ func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16 } func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) { - r1, _, e1 := syscall.Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) + r1, _, e1 := syscall.SyscallN(procCryptGenRandom.Addr(), uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) if r1 == 0 { err = errnoErr(e1) } @@ -744,7 +750,7 @@ func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) { } func CryptReleaseContext(provhandle Handle, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procCryptReleaseContext.Addr(), uintptr(provhandle), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -752,7 +758,7 @@ func CryptReleaseContext(provhandle Handle, flags uint32) (err error) { } func DeleteService(service Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDeleteService.Addr(), 1, uintptr(service), 0, 0) + r1, _, e1 := syscall.SyscallN(procDeleteService.Addr(), uintptr(service)) if r1 == 0 { err = errnoErr(e1) } @@ -760,7 +766,7 @@ func DeleteService(service Handle) (err error) { } func DeregisterEventSource(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDeregisterEventSource.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procDeregisterEventSource.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -768,7 +774,7 @@ func DeregisterEventSource(handle Handle) (err error) { } func DuplicateTokenEx(existingToken Token, desiredAccess uint32, tokenAttributes *SecurityAttributes, impersonationLevel uint32, tokenType uint32, newToken *Token) (err error) { - r1, _, e1 := syscall.Syscall6(procDuplicateTokenEx.Addr(), 6, uintptr(existingToken), uintptr(desiredAccess), uintptr(unsafe.Pointer(tokenAttributes)), uintptr(impersonationLevel), uintptr(tokenType), uintptr(unsafe.Pointer(newToken))) + r1, _, e1 := syscall.SyscallN(procDuplicateTokenEx.Addr(), uintptr(existingToken), uintptr(desiredAccess), uintptr(unsafe.Pointer(tokenAttributes)), uintptr(impersonationLevel), uintptr(tokenType), uintptr(unsafe.Pointer(newToken))) if r1 == 0 { err = errnoErr(e1) } @@ -776,7 +782,7 @@ func DuplicateTokenEx(existingToken Token, desiredAccess uint32, tokenAttributes } func EnumDependentServices(service Handle, activityState uint32, services *ENUM_SERVICE_STATUS, buffSize uint32, bytesNeeded *uint32, servicesReturned *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procEnumDependentServicesW.Addr(), 6, uintptr(service), uintptr(activityState), uintptr(unsafe.Pointer(services)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned))) + r1, _, e1 := syscall.SyscallN(procEnumDependentServicesW.Addr(), uintptr(service), uintptr(activityState), uintptr(unsafe.Pointer(services)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned))) if r1 == 0 { err = errnoErr(e1) } @@ -784,7 +790,7 @@ func EnumDependentServices(service Handle, activityState uint32, services *ENUM_ } func EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) { - r1, _, e1 := syscall.Syscall12(procEnumServicesStatusExW.Addr(), 10, uintptr(mgr), uintptr(infoLevel), uintptr(serviceType), uintptr(serviceState), uintptr(unsafe.Pointer(services)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned)), uintptr(unsafe.Pointer(resumeHandle)), uintptr(unsafe.Pointer(groupName)), 0, 0) + r1, _, e1 := syscall.SyscallN(procEnumServicesStatusExW.Addr(), uintptr(mgr), uintptr(infoLevel), uintptr(serviceType), uintptr(serviceState), uintptr(unsafe.Pointer(services)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned)), uintptr(unsafe.Pointer(resumeHandle)), uintptr(unsafe.Pointer(groupName))) if r1 == 0 { err = errnoErr(e1) } @@ -792,13 +798,13 @@ func EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serv } func EqualSid(sid1 *SID, sid2 *SID) (isEqual bool) { - r0, _, _ := syscall.Syscall(procEqualSid.Addr(), 2, uintptr(unsafe.Pointer(sid1)), uintptr(unsafe.Pointer(sid2)), 0) + r0, _, _ := syscall.SyscallN(procEqualSid.Addr(), uintptr(unsafe.Pointer(sid1)), uintptr(unsafe.Pointer(sid2))) isEqual = r0 != 0 return } func FreeSid(sid *SID) (err error) { - r1, _, e1 := syscall.Syscall(procFreeSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + r1, _, e1 := syscall.SyscallN(procFreeSid.Addr(), uintptr(unsafe.Pointer(sid))) if r1 != 0 { err = errnoErr(e1) } @@ -806,7 +812,7 @@ func FreeSid(sid *SID) (err error) { } func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (err error) { - r1, _, e1 := syscall.Syscall(procGetAce.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce))) + r1, _, e1 := syscall.SyscallN(procGetAce.Addr(), uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce))) if r1 == 0 { err = errnoErr(e1) } @@ -814,7 +820,7 @@ func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (err error) { } func GetLengthSid(sid *SID) (len uint32) { - r0, _, _ := syscall.Syscall(procGetLengthSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetLengthSid.Addr(), uintptr(unsafe.Pointer(sid))) len = uint32(r0) return } @@ -829,7 +835,7 @@ func getNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, security } func _getNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) { - r0, _, _ := syscall.Syscall9(procGetNamedSecurityInfoW.Addr(), 8, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd)), 0) + r0, _, _ := syscall.SyscallN(procGetNamedSecurityInfoW.Addr(), uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -837,7 +843,7 @@ func _getNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securi } func getSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, control *SECURITY_DESCRIPTOR_CONTROL, revision *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetSecurityDescriptorControl.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(control)), uintptr(unsafe.Pointer(revision))) + r1, _, e1 := syscall.SyscallN(procGetSecurityDescriptorControl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(control)), uintptr(unsafe.Pointer(revision))) if r1 == 0 { err = errnoErr(e1) } @@ -853,7 +859,7 @@ func getSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent *bool, dacl if *daclDefaulted { _p1 = 1 } - r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(&_p0)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(&_p1)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetSecurityDescriptorDacl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(&_p0)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(&_p1))) *daclPresent = _p0 != 0 *daclDefaulted = _p1 != 0 if r1 == 0 { @@ -867,7 +873,7 @@ func getSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group **SID, groupDefau if *groupDefaulted { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procGetSecurityDescriptorGroup.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(&_p0))) + r1, _, e1 := syscall.SyscallN(procGetSecurityDescriptorGroup.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(&_p0))) *groupDefaulted = _p0 != 0 if r1 == 0 { err = errnoErr(e1) @@ -876,7 +882,7 @@ func getSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group **SID, groupDefau } func getSecurityDescriptorLength(sd *SECURITY_DESCRIPTOR) (len uint32) { - r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(unsafe.Pointer(sd)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetSecurityDescriptorLength.Addr(), uintptr(unsafe.Pointer(sd))) len = uint32(r0) return } @@ -886,7 +892,7 @@ func getSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner **SID, ownerDefau if *ownerDefaulted { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procGetSecurityDescriptorOwner.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(&_p0))) + r1, _, e1 := syscall.SyscallN(procGetSecurityDescriptorOwner.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(&_p0))) *ownerDefaulted = _p0 != 0 if r1 == 0 { err = errnoErr(e1) @@ -895,7 +901,7 @@ func getSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner **SID, ownerDefau } func getSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) (ret error) { - r0, _, _ := syscall.Syscall(procGetSecurityDescriptorRMControl.Addr(), 2, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(rmControl)), 0) + r0, _, _ := syscall.SyscallN(procGetSecurityDescriptorRMControl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(rmControl))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -911,7 +917,7 @@ func getSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent *bool, sacl if *saclDefaulted { _p1 = 1 } - r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorSacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(&_p0)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(&_p1)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetSecurityDescriptorSacl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(&_p0)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(&_p1))) *saclPresent = _p0 != 0 *saclDefaulted = _p1 != 0 if r1 == 0 { @@ -921,7 +927,7 @@ func getSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent *bool, sacl } func getSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) { - r0, _, _ := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd)), 0) + r0, _, _ := syscall.SyscallN(procGetSecurityInfo.Addr(), uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -929,25 +935,25 @@ func getSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformati } func getSidIdentifierAuthority(sid *SID) (authority *SidIdentifierAuthority) { - r0, _, _ := syscall.Syscall(procGetSidIdentifierAuthority.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetSidIdentifierAuthority.Addr(), uintptr(unsafe.Pointer(sid))) authority = (*SidIdentifierAuthority)(unsafe.Pointer(r0)) return } func getSidSubAuthority(sid *SID, index uint32) (subAuthority *uint32) { - r0, _, _ := syscall.Syscall(procGetSidSubAuthority.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(index), 0) + r0, _, _ := syscall.SyscallN(procGetSidSubAuthority.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(index)) subAuthority = (*uint32)(unsafe.Pointer(r0)) return } func getSidSubAuthorityCount(sid *SID) (count *uint8) { - r0, _, _ := syscall.Syscall(procGetSidSubAuthorityCount.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetSidSubAuthorityCount.Addr(), uintptr(unsafe.Pointer(sid))) count = (*uint8)(unsafe.Pointer(r0)) return } func GetTokenInformation(token Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetTokenInformation.Addr(), 5, uintptr(token), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0) + r1, _, e1 := syscall.SyscallN(procGetTokenInformation.Addr(), uintptr(token), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen))) if r1 == 0 { err = errnoErr(e1) } @@ -955,7 +961,7 @@ func GetTokenInformation(token Token, infoClass uint32, info *byte, infoLen uint } func ImpersonateSelf(impersonationlevel uint32) (err error) { - r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0) + r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(impersonationlevel)) if r1 == 0 { err = errnoErr(e1) } @@ -963,7 +969,7 @@ func ImpersonateSelf(impersonationlevel uint32) (err error) { } func initializeSecurityDescriptor(absoluteSD *SECURITY_DESCRIPTOR, revision uint32) (err error) { - r1, _, e1 := syscall.Syscall(procInitializeSecurityDescriptor.Addr(), 2, uintptr(unsafe.Pointer(absoluteSD)), uintptr(revision), 0) + r1, _, e1 := syscall.SyscallN(procInitializeSecurityDescriptor.Addr(), uintptr(unsafe.Pointer(absoluteSD)), uintptr(revision)) if r1 == 0 { err = errnoErr(e1) } @@ -979,7 +985,7 @@ func InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint if rebootAfterShutdown { _p1 = 1 } - r1, _, e1 := syscall.Syscall6(procInitiateSystemShutdownExW.Addr(), 6, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(message)), uintptr(timeout), uintptr(_p0), uintptr(_p1), uintptr(reason)) + r1, _, e1 := syscall.SyscallN(procInitiateSystemShutdownExW.Addr(), uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(message)), uintptr(timeout), uintptr(_p0), uintptr(_p1), uintptr(reason)) if r1 == 0 { err = errnoErr(e1) } @@ -987,7 +993,7 @@ func InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint } func isTokenRestricted(tokenHandle Token) (ret bool, err error) { - r0, _, e1 := syscall.Syscall(procIsTokenRestricted.Addr(), 1, uintptr(tokenHandle), 0, 0) + r0, _, e1 := syscall.SyscallN(procIsTokenRestricted.Addr(), uintptr(tokenHandle)) ret = r0 != 0 if !ret { err = errnoErr(e1) @@ -996,25 +1002,25 @@ func isTokenRestricted(tokenHandle Token) (ret bool, err error) { } func isValidSecurityDescriptor(sd *SECURITY_DESCRIPTOR) (isValid bool) { - r0, _, _ := syscall.Syscall(procIsValidSecurityDescriptor.Addr(), 1, uintptr(unsafe.Pointer(sd)), 0, 0) + r0, _, _ := syscall.SyscallN(procIsValidSecurityDescriptor.Addr(), uintptr(unsafe.Pointer(sd))) isValid = r0 != 0 return } func isValidSid(sid *SID) (isValid bool) { - r0, _, _ := syscall.Syscall(procIsValidSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + r0, _, _ := syscall.SyscallN(procIsValidSid.Addr(), uintptr(unsafe.Pointer(sid))) isValid = r0 != 0 return } func isWellKnownSid(sid *SID, sidType WELL_KNOWN_SID_TYPE) (isWellKnown bool) { - r0, _, _ := syscall.Syscall(procIsWellKnownSid.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(sidType), 0) + r0, _, _ := syscall.SyscallN(procIsWellKnownSid.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(sidType)) isWellKnown = r0 != 0 return } func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0) + r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use))) if r1 == 0 { err = errnoErr(e1) } @@ -1022,7 +1028,7 @@ func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen } func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0) + r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use))) if r1 == 0 { err = errnoErr(e1) } @@ -1030,7 +1036,7 @@ func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint3 } func LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) { - r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemname)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) + r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemname)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) if r1 == 0 { err = errnoErr(e1) } @@ -1038,7 +1044,7 @@ func LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err err } func makeAbsoluteSD(selfRelativeSD *SECURITY_DESCRIPTOR, absoluteSD *SECURITY_DESCRIPTOR, absoluteSDSize *uint32, dacl *ACL, daclSize *uint32, sacl *ACL, saclSize *uint32, owner *SID, ownerSize *uint32, group *SID, groupSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall12(procMakeAbsoluteSD.Addr(), 11, uintptr(unsafe.Pointer(selfRelativeSD)), uintptr(unsafe.Pointer(absoluteSD)), uintptr(unsafe.Pointer(absoluteSDSize)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclSize)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(saclSize)), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(ownerSize)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(groupSize)), 0) + r1, _, e1 := syscall.SyscallN(procMakeAbsoluteSD.Addr(), uintptr(unsafe.Pointer(selfRelativeSD)), uintptr(unsafe.Pointer(absoluteSD)), uintptr(unsafe.Pointer(absoluteSDSize)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclSize)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(saclSize)), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(ownerSize)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(groupSize))) if r1 == 0 { err = errnoErr(e1) } @@ -1046,7 +1052,7 @@ func makeAbsoluteSD(selfRelativeSD *SECURITY_DESCRIPTOR, absoluteSD *SECURITY_DE } func makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procMakeSelfRelativeSD.Addr(), 3, uintptr(unsafe.Pointer(absoluteSD)), uintptr(unsafe.Pointer(selfRelativeSD)), uintptr(unsafe.Pointer(selfRelativeSDSize))) + r1, _, e1 := syscall.SyscallN(procMakeSelfRelativeSD.Addr(), uintptr(unsafe.Pointer(absoluteSD)), uintptr(unsafe.Pointer(selfRelativeSD)), uintptr(unsafe.Pointer(selfRelativeSDSize))) if r1 == 0 { err = errnoErr(e1) } @@ -1054,7 +1060,7 @@ func makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURIT } func NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) { - r0, _, _ := syscall.Syscall(procNotifyServiceStatusChangeW.Addr(), 3, uintptr(service), uintptr(notifyMask), uintptr(unsafe.Pointer(notifier))) + r0, _, _ := syscall.SyscallN(procNotifyServiceStatusChangeW.Addr(), uintptr(service), uintptr(notifyMask), uintptr(unsafe.Pointer(notifier))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -1062,7 +1068,7 @@ func NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERV } func OpenProcessToken(process Handle, access uint32, token *Token) (err error) { - r1, _, e1 := syscall.Syscall(procOpenProcessToken.Addr(), 3, uintptr(process), uintptr(access), uintptr(unsafe.Pointer(token))) + r1, _, e1 := syscall.SyscallN(procOpenProcessToken.Addr(), uintptr(process), uintptr(access), uintptr(unsafe.Pointer(token))) if r1 == 0 { err = errnoErr(e1) } @@ -1070,7 +1076,7 @@ func OpenProcessToken(process Handle, access uint32, token *Token) (err error) { } func OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procOpenSCManagerW.Addr(), 3, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(databaseName)), uintptr(access)) + r0, _, e1 := syscall.SyscallN(procOpenSCManagerW.Addr(), uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(databaseName)), uintptr(access)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1079,7 +1085,7 @@ func OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (ha } func OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procOpenServiceW.Addr(), 3, uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(access)) + r0, _, e1 := syscall.SyscallN(procOpenServiceW.Addr(), uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(access)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1092,7 +1098,7 @@ func OpenThreadToken(thread Handle, access uint32, openAsSelf bool, token *Token if openAsSelf { _p0 = 1 } - r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(access), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) + r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(access), uintptr(_p0), uintptr(unsafe.Pointer(token))) if r1 == 0 { err = errnoErr(e1) } @@ -1100,7 +1106,7 @@ func OpenThreadToken(thread Handle, access uint32, openAsSelf bool, token *Token } func QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryServiceConfig2W.Addr(), 5, uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(buff)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded)), 0) + r1, _, e1 := syscall.SyscallN(procQueryServiceConfig2W.Addr(), uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(buff)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded))) if r1 == 0 { err = errnoErr(e1) } @@ -1108,7 +1114,7 @@ func QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize } func QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, bufSize uint32, bytesNeeded *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryServiceConfigW.Addr(), 4, uintptr(service), uintptr(unsafe.Pointer(serviceConfig)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0) + r1, _, e1 := syscall.SyscallN(procQueryServiceConfigW.Addr(), uintptr(service), uintptr(unsafe.Pointer(serviceConfig)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded))) if r1 == 0 { err = errnoErr(e1) } @@ -1120,7 +1126,7 @@ func QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInf if err != nil { return } - r1, _, e1 := syscall.Syscall(procQueryServiceDynamicInformation.Addr(), 3, uintptr(service), uintptr(infoLevel), uintptr(dynamicInfo)) + r1, _, e1 := syscall.SyscallN(procQueryServiceDynamicInformation.Addr(), uintptr(service), uintptr(infoLevel), uintptr(dynamicInfo)) if r1 == 0 { err = errnoErr(e1) } @@ -1128,7 +1134,7 @@ func QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInf } func QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryServiceLockStatusW.Addr(), 4, uintptr(mgr), uintptr(unsafe.Pointer(lockStatus)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0) + r1, _, e1 := syscall.SyscallN(procQueryServiceLockStatusW.Addr(), uintptr(mgr), uintptr(unsafe.Pointer(lockStatus)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded))) if r1 == 0 { err = errnoErr(e1) } @@ -1136,7 +1142,7 @@ func QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, b } func QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) { - r1, _, e1 := syscall.Syscall(procQueryServiceStatus.Addr(), 2, uintptr(service), uintptr(unsafe.Pointer(status)), 0) + r1, _, e1 := syscall.SyscallN(procQueryServiceStatus.Addr(), uintptr(service), uintptr(unsafe.Pointer(status))) if r1 == 0 { err = errnoErr(e1) } @@ -1144,7 +1150,7 @@ func QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) { } func QueryServiceStatusEx(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryServiceStatusEx.Addr(), 5, uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(buff)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded)), 0) + r1, _, e1 := syscall.SyscallN(procQueryServiceStatusEx.Addr(), uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(buff)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded))) if r1 == 0 { err = errnoErr(e1) } @@ -1152,7 +1158,7 @@ func QueryServiceStatusEx(service Handle, infoLevel uint32, buff *byte, buffSize } func RegCloseKey(key Handle) (regerrno error) { - r0, _, _ := syscall.Syscall(procRegCloseKey.Addr(), 1, uintptr(key), 0, 0) + r0, _, _ := syscall.SyscallN(procRegCloseKey.Addr(), uintptr(key)) if r0 != 0 { regerrno = syscall.Errno(r0) } @@ -1160,7 +1166,7 @@ func RegCloseKey(key Handle) (regerrno error) { } func RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) { - r0, _, _ := syscall.Syscall9(procRegEnumKeyExW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(lastWriteTime)), 0) + r0, _, _ := syscall.SyscallN(procRegEnumKeyExW.Addr(), uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(lastWriteTime))) if r0 != 0 { regerrno = syscall.Errno(r0) } @@ -1176,7 +1182,7 @@ func RegNotifyChangeKeyValue(key Handle, watchSubtree bool, notifyFilter uint32, if asynchronous { _p1 = 1 } - r0, _, _ := syscall.Syscall6(procRegNotifyChangeKeyValue.Addr(), 5, uintptr(key), uintptr(_p0), uintptr(notifyFilter), uintptr(event), uintptr(_p1), 0) + r0, _, _ := syscall.SyscallN(procRegNotifyChangeKeyValue.Addr(), uintptr(key), uintptr(_p0), uintptr(notifyFilter), uintptr(event), uintptr(_p1)) if r0 != 0 { regerrno = syscall.Errno(r0) } @@ -1184,7 +1190,7 @@ func RegNotifyChangeKeyValue(key Handle, watchSubtree bool, notifyFilter uint32, } func RegOpenKeyEx(key Handle, subkey *uint16, options uint32, desiredAccess uint32, result *Handle) (regerrno error) { - r0, _, _ := syscall.Syscall6(procRegOpenKeyExW.Addr(), 5, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(options), uintptr(desiredAccess), uintptr(unsafe.Pointer(result)), 0) + r0, _, _ := syscall.SyscallN(procRegOpenKeyExW.Addr(), uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(options), uintptr(desiredAccess), uintptr(unsafe.Pointer(result))) if r0 != 0 { regerrno = syscall.Errno(r0) } @@ -1192,7 +1198,7 @@ func RegOpenKeyEx(key Handle, subkey *uint16, options uint32, desiredAccess uint } func RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) { - r0, _, _ := syscall.Syscall12(procRegQueryInfoKeyW.Addr(), 12, uintptr(key), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(subkeysLen)), uintptr(unsafe.Pointer(maxSubkeyLen)), uintptr(unsafe.Pointer(maxClassLen)), uintptr(unsafe.Pointer(valuesLen)), uintptr(unsafe.Pointer(maxValueNameLen)), uintptr(unsafe.Pointer(maxValueLen)), uintptr(unsafe.Pointer(saLen)), uintptr(unsafe.Pointer(lastWriteTime))) + r0, _, _ := syscall.SyscallN(procRegQueryInfoKeyW.Addr(), uintptr(key), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(subkeysLen)), uintptr(unsafe.Pointer(maxSubkeyLen)), uintptr(unsafe.Pointer(maxClassLen)), uintptr(unsafe.Pointer(valuesLen)), uintptr(unsafe.Pointer(maxValueNameLen)), uintptr(unsafe.Pointer(maxValueLen)), uintptr(unsafe.Pointer(saLen)), uintptr(unsafe.Pointer(lastWriteTime))) if r0 != 0 { regerrno = syscall.Errno(r0) } @@ -1200,7 +1206,7 @@ func RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint } func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) { - r0, _, _ := syscall.Syscall6(procRegQueryValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen))) + r0, _, _ := syscall.SyscallN(procRegQueryValueExW.Addr(), uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen))) if r0 != 0 { regerrno = syscall.Errno(r0) } @@ -1208,7 +1214,7 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32 } func RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procRegisterEventSourceW.Addr(), 2, uintptr(unsafe.Pointer(uncServerName)), uintptr(unsafe.Pointer(sourceName)), 0) + r0, _, e1 := syscall.SyscallN(procRegisterEventSourceW.Addr(), uintptr(unsafe.Pointer(uncServerName)), uintptr(unsafe.Pointer(sourceName))) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1217,7 +1223,7 @@ func RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Hand } func RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procRegisterServiceCtrlHandlerExW.Addr(), 3, uintptr(unsafe.Pointer(serviceName)), uintptr(handlerProc), uintptr(context)) + r0, _, e1 := syscall.SyscallN(procRegisterServiceCtrlHandlerExW.Addr(), uintptr(unsafe.Pointer(serviceName)), uintptr(handlerProc), uintptr(context)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1226,7 +1232,7 @@ func RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, cont } func ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) { - r1, _, e1 := syscall.Syscall9(procReportEventW.Addr(), 9, uintptr(log), uintptr(etype), uintptr(category), uintptr(eventId), uintptr(usrSId), uintptr(numStrings), uintptr(dataSize), uintptr(unsafe.Pointer(strings)), uintptr(unsafe.Pointer(rawData))) + r1, _, e1 := syscall.SyscallN(procReportEventW.Addr(), uintptr(log), uintptr(etype), uintptr(category), uintptr(eventId), uintptr(usrSId), uintptr(numStrings), uintptr(dataSize), uintptr(unsafe.Pointer(strings)), uintptr(unsafe.Pointer(rawData))) if r1 == 0 { err = errnoErr(e1) } @@ -1234,7 +1240,7 @@ func ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrS } func RevertToSelf() (err error) { - r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) + r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr()) if r1 == 0 { err = errnoErr(e1) } @@ -1242,7 +1248,7 @@ func RevertToSelf() (err error) { } func setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) { - r0, _, _ := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(countExplicitEntries), uintptr(unsafe.Pointer(explicitEntries)), uintptr(unsafe.Pointer(oldACL)), uintptr(unsafe.Pointer(newACL)), 0, 0) + r0, _, _ := syscall.SyscallN(procSetEntriesInAclW.Addr(), uintptr(countExplicitEntries), uintptr(unsafe.Pointer(explicitEntries)), uintptr(unsafe.Pointer(oldACL)), uintptr(unsafe.Pointer(newACL))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -1250,7 +1256,7 @@ func setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCE } func SetKernelObjectSecurity(handle Handle, securityInformation SECURITY_INFORMATION, securityDescriptor *SECURITY_DESCRIPTOR) (err error) { - r1, _, e1 := syscall.Syscall(procSetKernelObjectSecurity.Addr(), 3, uintptr(handle), uintptr(securityInformation), uintptr(unsafe.Pointer(securityDescriptor))) + r1, _, e1 := syscall.SyscallN(procSetKernelObjectSecurity.Addr(), uintptr(handle), uintptr(securityInformation), uintptr(unsafe.Pointer(securityDescriptor))) if r1 == 0 { err = errnoErr(e1) } @@ -1267,7 +1273,7 @@ func SetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, security } func _SetNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) (ret error) { - r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfoW.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0) + r0, _, _ := syscall.SyscallN(procSetNamedSecurityInfoW.Addr(), uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -1275,7 +1281,7 @@ func _SetNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securi } func setSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, controlBitsOfInterest SECURITY_DESCRIPTOR_CONTROL, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) (err error) { - r1, _, e1 := syscall.Syscall(procSetSecurityDescriptorControl.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(controlBitsOfInterest), uintptr(controlBitsToSet)) + r1, _, e1 := syscall.SyscallN(procSetSecurityDescriptorControl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(controlBitsOfInterest), uintptr(controlBitsToSet)) if r1 == 0 { err = errnoErr(e1) } @@ -1291,7 +1297,7 @@ func setSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent bool, dacl * if daclDefaulted { _p1 = 1 } - r1, _, e1 := syscall.Syscall6(procSetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(_p0), uintptr(unsafe.Pointer(dacl)), uintptr(_p1), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetSecurityDescriptorDacl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(_p0), uintptr(unsafe.Pointer(dacl)), uintptr(_p1)) if r1 == 0 { err = errnoErr(e1) } @@ -1303,7 +1309,7 @@ func setSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group *SID, groupDefaul if groupDefaulted { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procSetSecurityDescriptorGroup.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(group)), uintptr(_p0)) + r1, _, e1 := syscall.SyscallN(procSetSecurityDescriptorGroup.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(group)), uintptr(_p0)) if r1 == 0 { err = errnoErr(e1) } @@ -1315,7 +1321,7 @@ func setSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner *SID, ownerDefaul if ownerDefaulted { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procSetSecurityDescriptorOwner.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(owner)), uintptr(_p0)) + r1, _, e1 := syscall.SyscallN(procSetSecurityDescriptorOwner.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(owner)), uintptr(_p0)) if r1 == 0 { err = errnoErr(e1) } @@ -1323,7 +1329,7 @@ func setSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner *SID, ownerDefaul } func setSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) { - syscall.Syscall(procSetSecurityDescriptorRMControl.Addr(), 2, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(rmControl)), 0) + syscall.SyscallN(procSetSecurityDescriptorRMControl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(rmControl))) return } @@ -1336,7 +1342,7 @@ func setSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent bool, sacl * if saclDefaulted { _p1 = 1 } - r1, _, e1 := syscall.Syscall6(procSetSecurityDescriptorSacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(_p0), uintptr(unsafe.Pointer(sacl)), uintptr(_p1), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetSecurityDescriptorSacl.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(_p0), uintptr(unsafe.Pointer(sacl)), uintptr(_p1)) if r1 == 0 { err = errnoErr(e1) } @@ -1344,7 +1350,7 @@ func setSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent bool, sacl * } func SetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) (ret error) { - r0, _, _ := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0) + r0, _, _ := syscall.SyscallN(procSetSecurityInfo.Addr(), uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -1352,7 +1358,7 @@ func SetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformati } func SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) { - r1, _, e1 := syscall.Syscall(procSetServiceStatus.Addr(), 2, uintptr(service), uintptr(unsafe.Pointer(serviceStatus)), 0) + r1, _, e1 := syscall.SyscallN(procSetServiceStatus.Addr(), uintptr(service), uintptr(unsafe.Pointer(serviceStatus))) if r1 == 0 { err = errnoErr(e1) } @@ -1360,7 +1366,7 @@ func SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) } func SetThreadToken(thread *Handle, token Token) (err error) { - r1, _, e1 := syscall.Syscall(procSetThreadToken.Addr(), 2, uintptr(unsafe.Pointer(thread)), uintptr(token), 0) + r1, _, e1 := syscall.SyscallN(procSetThreadToken.Addr(), uintptr(unsafe.Pointer(thread)), uintptr(token)) if r1 == 0 { err = errnoErr(e1) } @@ -1368,7 +1374,7 @@ func SetThreadToken(thread *Handle, token Token) (err error) { } func SetTokenInformation(token Token, infoClass uint32, info *byte, infoLen uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetTokenInformation.Addr(), 4, uintptr(token), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetTokenInformation.Addr(), uintptr(token), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen)) if r1 == 0 { err = errnoErr(e1) } @@ -1376,7 +1382,7 @@ func SetTokenInformation(token Token, infoClass uint32, info *byte, infoLen uint } func StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) { - r1, _, e1 := syscall.Syscall(procStartServiceCtrlDispatcherW.Addr(), 1, uintptr(unsafe.Pointer(serviceTable)), 0, 0) + r1, _, e1 := syscall.SyscallN(procStartServiceCtrlDispatcherW.Addr(), uintptr(unsafe.Pointer(serviceTable))) if r1 == 0 { err = errnoErr(e1) } @@ -1384,7 +1390,7 @@ func StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) { } func StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) { - r1, _, e1 := syscall.Syscall(procStartServiceW.Addr(), 3, uintptr(service), uintptr(numArgs), uintptr(unsafe.Pointer(argVectors))) + r1, _, e1 := syscall.SyscallN(procStartServiceW.Addr(), uintptr(service), uintptr(numArgs), uintptr(unsafe.Pointer(argVectors))) if r1 == 0 { err = errnoErr(e1) } @@ -1392,7 +1398,7 @@ func StartService(service Handle, numArgs uint32, argVectors **uint16) (err erro } func CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) { - r1, _, e1 := syscall.Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCertAddCertificateContextToStore.Addr(), uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext))) if r1 == 0 { err = errnoErr(e1) } @@ -1400,7 +1406,7 @@ func CertAddCertificateContextToStore(store Handle, certContext *CertContext, ad } func CertCloseStore(store Handle, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procCertCloseStore.Addr(), uintptr(store), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -1408,7 +1414,7 @@ func CertCloseStore(store Handle, flags uint32) (err error) { } func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, encodedLen uint32) (context *CertContext, err error) { - r0, _, e1 := syscall.Syscall(procCertCreateCertificateContext.Addr(), 3, uintptr(certEncodingType), uintptr(unsafe.Pointer(certEncoded)), uintptr(encodedLen)) + r0, _, e1 := syscall.SyscallN(procCertCreateCertificateContext.Addr(), uintptr(certEncodingType), uintptr(unsafe.Pointer(certEncoded)), uintptr(encodedLen)) context = (*CertContext)(unsafe.Pointer(r0)) if context == nil { err = errnoErr(e1) @@ -1417,7 +1423,7 @@ func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, en } func CertDeleteCertificateFromStore(certContext *CertContext) (err error) { - r1, _, e1 := syscall.Syscall(procCertDeleteCertificateFromStore.Addr(), 1, uintptr(unsafe.Pointer(certContext)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCertDeleteCertificateFromStore.Addr(), uintptr(unsafe.Pointer(certContext))) if r1 == 0 { err = errnoErr(e1) } @@ -1425,13 +1431,13 @@ func CertDeleteCertificateFromStore(certContext *CertContext) (err error) { } func CertDuplicateCertificateContext(certContext *CertContext) (dupContext *CertContext) { - r0, _, _ := syscall.Syscall(procCertDuplicateCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(certContext)), 0, 0) + r0, _, _ := syscall.SyscallN(procCertDuplicateCertificateContext.Addr(), uintptr(unsafe.Pointer(certContext))) dupContext = (*CertContext)(unsafe.Pointer(r0)) return } func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (context *CertContext, err error) { - r0, _, e1 := syscall.Syscall(procCertEnumCertificatesInStore.Addr(), 2, uintptr(store), uintptr(unsafe.Pointer(prevContext)), 0) + r0, _, e1 := syscall.SyscallN(procCertEnumCertificatesInStore.Addr(), uintptr(store), uintptr(unsafe.Pointer(prevContext))) context = (*CertContext)(unsafe.Pointer(r0)) if context == nil { err = errnoErr(e1) @@ -1440,7 +1446,7 @@ func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (contex } func CertFindCertificateInStore(store Handle, certEncodingType uint32, findFlags uint32, findType uint32, findPara unsafe.Pointer, prevCertContext *CertContext) (cert *CertContext, err error) { - r0, _, e1 := syscall.Syscall6(procCertFindCertificateInStore.Addr(), 6, uintptr(store), uintptr(certEncodingType), uintptr(findFlags), uintptr(findType), uintptr(findPara), uintptr(unsafe.Pointer(prevCertContext))) + r0, _, e1 := syscall.SyscallN(procCertFindCertificateInStore.Addr(), uintptr(store), uintptr(certEncodingType), uintptr(findFlags), uintptr(findType), uintptr(findPara), uintptr(unsafe.Pointer(prevCertContext))) cert = (*CertContext)(unsafe.Pointer(r0)) if cert == nil { err = errnoErr(e1) @@ -1449,7 +1455,7 @@ func CertFindCertificateInStore(store Handle, certEncodingType uint32, findFlags } func CertFindChainInStore(store Handle, certEncodingType uint32, findFlags uint32, findType uint32, findPara unsafe.Pointer, prevChainContext *CertChainContext) (certchain *CertChainContext, err error) { - r0, _, e1 := syscall.Syscall6(procCertFindChainInStore.Addr(), 6, uintptr(store), uintptr(certEncodingType), uintptr(findFlags), uintptr(findType), uintptr(findPara), uintptr(unsafe.Pointer(prevChainContext))) + r0, _, e1 := syscall.SyscallN(procCertFindChainInStore.Addr(), uintptr(store), uintptr(certEncodingType), uintptr(findFlags), uintptr(findType), uintptr(findPara), uintptr(unsafe.Pointer(prevChainContext))) certchain = (*CertChainContext)(unsafe.Pointer(r0)) if certchain == nil { err = errnoErr(e1) @@ -1458,18 +1464,18 @@ func CertFindChainInStore(store Handle, certEncodingType uint32, findFlags uint3 } func CertFindExtension(objId *byte, countExtensions uint32, extensions *CertExtension) (ret *CertExtension) { - r0, _, _ := syscall.Syscall(procCertFindExtension.Addr(), 3, uintptr(unsafe.Pointer(objId)), uintptr(countExtensions), uintptr(unsafe.Pointer(extensions))) + r0, _, _ := syscall.SyscallN(procCertFindExtension.Addr(), uintptr(unsafe.Pointer(objId)), uintptr(countExtensions), uintptr(unsafe.Pointer(extensions))) ret = (*CertExtension)(unsafe.Pointer(r0)) return } func CertFreeCertificateChain(ctx *CertChainContext) { - syscall.Syscall(procCertFreeCertificateChain.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0) + syscall.SyscallN(procCertFreeCertificateChain.Addr(), uintptr(unsafe.Pointer(ctx))) return } func CertFreeCertificateContext(ctx *CertContext) (err error) { - r1, _, e1 := syscall.Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCertFreeCertificateContext.Addr(), uintptr(unsafe.Pointer(ctx))) if r1 == 0 { err = errnoErr(e1) } @@ -1477,7 +1483,7 @@ func CertFreeCertificateContext(ctx *CertContext) (err error) { } func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, additionalStore Handle, para *CertChainPara, flags uint32, reserved uintptr, chainCtx **CertChainContext) (err error) { - r1, _, e1 := syscall.Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0) + r1, _, e1 := syscall.SyscallN(procCertGetCertificateChain.Addr(), uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx))) if r1 == 0 { err = errnoErr(e1) } @@ -1485,13 +1491,13 @@ func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, a } func CertGetNameString(certContext *CertContext, nameType uint32, flags uint32, typePara unsafe.Pointer, name *uint16, size uint32) (chars uint32) { - r0, _, _ := syscall.Syscall6(procCertGetNameStringW.Addr(), 6, uintptr(unsafe.Pointer(certContext)), uintptr(nameType), uintptr(flags), uintptr(typePara), uintptr(unsafe.Pointer(name)), uintptr(size)) + r0, _, _ := syscall.SyscallN(procCertGetNameStringW.Addr(), uintptr(unsafe.Pointer(certContext)), uintptr(nameType), uintptr(flags), uintptr(typePara), uintptr(unsafe.Pointer(name)), uintptr(size)) chars = uint32(r0) return } func CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptProv uintptr, flags uint32, para uintptr) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCertOpenStore.Addr(), 5, uintptr(storeProvider), uintptr(msgAndCertEncodingType), uintptr(cryptProv), uintptr(flags), uintptr(para), 0) + r0, _, e1 := syscall.SyscallN(procCertOpenStore.Addr(), uintptr(storeProvider), uintptr(msgAndCertEncodingType), uintptr(cryptProv), uintptr(flags), uintptr(para)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1500,7 +1506,7 @@ func CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptPr } func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, err error) { - r0, _, e1 := syscall.Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0) + r0, _, e1 := syscall.SyscallN(procCertOpenSystemStoreW.Addr(), uintptr(hprov), uintptr(unsafe.Pointer(name))) store = Handle(r0) if store == 0 { err = errnoErr(e1) @@ -1509,7 +1515,7 @@ func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, err error) { } func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext, para *CertChainPolicyPara, status *CertChainPolicyStatus) (err error) { - r1, _, e1 := syscall.Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCertVerifyCertificateChainPolicy.Addr(), uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status))) if r1 == 0 { err = errnoErr(e1) } @@ -1521,7 +1527,7 @@ func CryptAcquireCertificatePrivateKey(cert *CertContext, flags uint32, paramete if *callerFreeProvOrNCryptKey { _p0 = 1 } - r1, _, e1 := syscall.Syscall6(procCryptAcquireCertificatePrivateKey.Addr(), 6, uintptr(unsafe.Pointer(cert)), uintptr(flags), uintptr(parameters), uintptr(unsafe.Pointer(cryptProvOrNCryptKey)), uintptr(unsafe.Pointer(keySpec)), uintptr(unsafe.Pointer(&_p0))) + r1, _, e1 := syscall.SyscallN(procCryptAcquireCertificatePrivateKey.Addr(), uintptr(unsafe.Pointer(cert)), uintptr(flags), uintptr(parameters), uintptr(unsafe.Pointer(cryptProvOrNCryptKey)), uintptr(unsafe.Pointer(keySpec)), uintptr(unsafe.Pointer(&_p0))) *callerFreeProvOrNCryptKey = _p0 != 0 if r1 == 0 { err = errnoErr(e1) @@ -1530,7 +1536,7 @@ func CryptAcquireCertificatePrivateKey(cert *CertContext, flags uint32, paramete } func CryptDecodeObject(encodingType uint32, structType *byte, encodedBytes *byte, lenEncodedBytes uint32, flags uint32, decoded unsafe.Pointer, decodedLen *uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procCryptDecodeObject.Addr(), 7, uintptr(encodingType), uintptr(unsafe.Pointer(structType)), uintptr(unsafe.Pointer(encodedBytes)), uintptr(lenEncodedBytes), uintptr(flags), uintptr(decoded), uintptr(unsafe.Pointer(decodedLen)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCryptDecodeObject.Addr(), uintptr(encodingType), uintptr(unsafe.Pointer(structType)), uintptr(unsafe.Pointer(encodedBytes)), uintptr(lenEncodedBytes), uintptr(flags), uintptr(decoded), uintptr(unsafe.Pointer(decodedLen))) if r1 == 0 { err = errnoErr(e1) } @@ -1538,7 +1544,7 @@ func CryptDecodeObject(encodingType uint32, structType *byte, encodedBytes *byte } func CryptProtectData(dataIn *DataBlob, name *uint16, optionalEntropy *DataBlob, reserved uintptr, promptStruct *CryptProtectPromptStruct, flags uint32, dataOut *DataBlob) (err error) { - r1, _, e1 := syscall.Syscall9(procCryptProtectData.Addr(), 7, uintptr(unsafe.Pointer(dataIn)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(optionalEntropy)), uintptr(reserved), uintptr(unsafe.Pointer(promptStruct)), uintptr(flags), uintptr(unsafe.Pointer(dataOut)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCryptProtectData.Addr(), uintptr(unsafe.Pointer(dataIn)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(optionalEntropy)), uintptr(reserved), uintptr(unsafe.Pointer(promptStruct)), uintptr(flags), uintptr(unsafe.Pointer(dataOut))) if r1 == 0 { err = errnoErr(e1) } @@ -1546,7 +1552,7 @@ func CryptProtectData(dataIn *DataBlob, name *uint16, optionalEntropy *DataBlob, } func CryptQueryObject(objectType uint32, object unsafe.Pointer, expectedContentTypeFlags uint32, expectedFormatTypeFlags uint32, flags uint32, msgAndCertEncodingType *uint32, contentType *uint32, formatType *uint32, certStore *Handle, msg *Handle, context *unsafe.Pointer) (err error) { - r1, _, e1 := syscall.Syscall12(procCryptQueryObject.Addr(), 11, uintptr(objectType), uintptr(object), uintptr(expectedContentTypeFlags), uintptr(expectedFormatTypeFlags), uintptr(flags), uintptr(unsafe.Pointer(msgAndCertEncodingType)), uintptr(unsafe.Pointer(contentType)), uintptr(unsafe.Pointer(formatType)), uintptr(unsafe.Pointer(certStore)), uintptr(unsafe.Pointer(msg)), uintptr(unsafe.Pointer(context)), 0) + r1, _, e1 := syscall.SyscallN(procCryptQueryObject.Addr(), uintptr(objectType), uintptr(object), uintptr(expectedContentTypeFlags), uintptr(expectedFormatTypeFlags), uintptr(flags), uintptr(unsafe.Pointer(msgAndCertEncodingType)), uintptr(unsafe.Pointer(contentType)), uintptr(unsafe.Pointer(formatType)), uintptr(unsafe.Pointer(certStore)), uintptr(unsafe.Pointer(msg)), uintptr(unsafe.Pointer(context))) if r1 == 0 { err = errnoErr(e1) } @@ -1554,7 +1560,7 @@ func CryptQueryObject(objectType uint32, object unsafe.Pointer, expectedContentT } func CryptUnprotectData(dataIn *DataBlob, name **uint16, optionalEntropy *DataBlob, reserved uintptr, promptStruct *CryptProtectPromptStruct, flags uint32, dataOut *DataBlob) (err error) { - r1, _, e1 := syscall.Syscall9(procCryptUnprotectData.Addr(), 7, uintptr(unsafe.Pointer(dataIn)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(optionalEntropy)), uintptr(reserved), uintptr(unsafe.Pointer(promptStruct)), uintptr(flags), uintptr(unsafe.Pointer(dataOut)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCryptUnprotectData.Addr(), uintptr(unsafe.Pointer(dataIn)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(optionalEntropy)), uintptr(reserved), uintptr(unsafe.Pointer(promptStruct)), uintptr(flags), uintptr(unsafe.Pointer(dataOut))) if r1 == 0 { err = errnoErr(e1) } @@ -1562,7 +1568,7 @@ func CryptUnprotectData(dataIn *DataBlob, name **uint16, optionalEntropy *DataBl } func PFXImportCertStore(pfx *CryptDataBlob, password *uint16, flags uint32) (store Handle, err error) { - r0, _, e1 := syscall.Syscall(procPFXImportCertStore.Addr(), 3, uintptr(unsafe.Pointer(pfx)), uintptr(unsafe.Pointer(password)), uintptr(flags)) + r0, _, e1 := syscall.SyscallN(procPFXImportCertStore.Addr(), uintptr(unsafe.Pointer(pfx)), uintptr(unsafe.Pointer(password)), uintptr(flags)) store = Handle(r0) if store == 0 { err = errnoErr(e1) @@ -1571,7 +1577,7 @@ func PFXImportCertStore(pfx *CryptDataBlob, password *uint16, flags uint32) (sto } func DnsNameCompare(name1 *uint16, name2 *uint16) (same bool) { - r0, _, _ := syscall.Syscall(procDnsNameCompare_W.Addr(), 2, uintptr(unsafe.Pointer(name1)), uintptr(unsafe.Pointer(name2)), 0) + r0, _, _ := syscall.SyscallN(procDnsNameCompare_W.Addr(), uintptr(unsafe.Pointer(name1)), uintptr(unsafe.Pointer(name2))) same = r0 != 0 return } @@ -1586,7 +1592,7 @@ func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSR } func _DnsQuery(name *uint16, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) { - r0, _, _ := syscall.Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(name)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) + r0, _, _ := syscall.SyscallN(procDnsQuery_W.Addr(), uintptr(unsafe.Pointer(name)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) if r0 != 0 { status = syscall.Errno(r0) } @@ -1594,12 +1600,12 @@ func _DnsQuery(name *uint16, qtype uint16, options uint32, extra *byte, qrs **DN } func DnsRecordListFree(rl *DNSRecord, freetype uint32) { - syscall.Syscall(procDnsRecordListFree.Addr(), 2, uintptr(unsafe.Pointer(rl)), uintptr(freetype), 0) + syscall.SyscallN(procDnsRecordListFree.Addr(), uintptr(unsafe.Pointer(rl)), uintptr(freetype)) return } func DwmGetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) { - r0, _, _ := syscall.Syscall6(procDwmGetWindowAttribute.Addr(), 4, uintptr(hwnd), uintptr(attribute), uintptr(value), uintptr(size), 0, 0) + r0, _, _ := syscall.SyscallN(procDwmGetWindowAttribute.Addr(), uintptr(hwnd), uintptr(attribute), uintptr(value), uintptr(size)) if r0 != 0 { ret = syscall.Errno(r0) } @@ -1607,7 +1613,7 @@ func DwmGetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, si } func DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) { - r0, _, _ := syscall.Syscall6(procDwmSetWindowAttribute.Addr(), 4, uintptr(hwnd), uintptr(attribute), uintptr(value), uintptr(size), 0, 0) + r0, _, _ := syscall.SyscallN(procDwmSetWindowAttribute.Addr(), uintptr(hwnd), uintptr(attribute), uintptr(value), uintptr(size)) if r0 != 0 { ret = syscall.Errno(r0) } @@ -1615,15 +1621,20 @@ func DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, si } func CancelMibChangeNotify2(notificationHandle Handle) (errcode error) { - r0, _, _ := syscall.Syscall(procCancelMibChangeNotify2.Addr(), 1, uintptr(notificationHandle), 0, 0) + r0, _, _ := syscall.SyscallN(procCancelMibChangeNotify2.Addr(), uintptr(notificationHandle)) if r0 != 0 { errcode = syscall.Errno(r0) } return } +func FreeMibTable(memory unsafe.Pointer) { + syscall.SyscallN(procFreeMibTable.Addr(), uintptr(memory)) + return +} + func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { - r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0) + r0, _, _ := syscall.SyscallN(procGetAdaptersAddresses.Addr(), uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1631,7 +1642,7 @@ func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapter } func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) { - r0, _, _ := syscall.Syscall(procGetAdaptersInfo.Addr(), 2, uintptr(unsafe.Pointer(ai)), uintptr(unsafe.Pointer(ol)), 0) + r0, _, _ := syscall.SyscallN(procGetAdaptersInfo.Addr(), uintptr(unsafe.Pointer(ai)), uintptr(unsafe.Pointer(ol))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1639,7 +1650,7 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) { } func getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) { - r0, _, _ := syscall.Syscall(procGetBestInterfaceEx.Addr(), 2, uintptr(sockaddr), uintptr(unsafe.Pointer(pdwBestIfIndex)), 0) + r0, _, _ := syscall.SyscallN(procGetBestInterfaceEx.Addr(), uintptr(sockaddr), uintptr(unsafe.Pointer(pdwBestIfIndex))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1647,7 +1658,7 @@ func getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcod } func GetIfEntry(pIfRow *MibIfRow) (errcode error) { - r0, _, _ := syscall.Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetIfEntry.Addr(), uintptr(unsafe.Pointer(pIfRow))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1655,7 +1666,23 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) { } func GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) { - r0, _, _ := syscall.Syscall(procGetIfEntry2Ex.Addr(), 2, uintptr(level), uintptr(unsafe.Pointer(row)), 0) + r0, _, _ := syscall.SyscallN(procGetIfEntry2Ex.Addr(), uintptr(level), uintptr(unsafe.Pointer(row))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func GetIpForwardEntry2(row *MibIpForwardRow2) (errcode error) { + r0, _, _ := syscall.SyscallN(procGetIpForwardEntry2.Addr(), uintptr(unsafe.Pointer(row))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func GetIpForwardTable2(family uint16, table **MibIpForwardTable2) (errcode error) { + r0, _, _ := syscall.SyscallN(procGetIpForwardTable2.Addr(), uintptr(family), uintptr(unsafe.Pointer(table))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1663,7 +1690,7 @@ func GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) { } func GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) { - r0, _, _ := syscall.Syscall(procGetUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetUnicastIpAddressEntry.Addr(), uintptr(unsafe.Pointer(row))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1675,7 +1702,19 @@ func NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsa if initialNotification { _p0 = 1 } - r0, _, _ := syscall.Syscall6(procNotifyIpInterfaceChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0) + r0, _, _ := syscall.SyscallN(procNotifyIpInterfaceChange.Addr(), uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func NotifyRouteChange2(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) { + var _p0 uint32 + if initialNotification { + _p0 = 1 + } + r0, _, _ := syscall.SyscallN(procNotifyRouteChange2.Addr(), uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1687,7 +1726,7 @@ func NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext if initialNotification { _p0 = 1 } - r0, _, _ := syscall.Syscall6(procNotifyUnicastIpAddressChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0) + r0, _, _ := syscall.SyscallN(procNotifyUnicastIpAddressChange.Addr(), uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle))) if r0 != 0 { errcode = syscall.Errno(r0) } @@ -1695,7 +1734,7 @@ func NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext } func AddDllDirectory(path *uint16) (cookie uintptr, err error) { - r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + r0, _, e1 := syscall.SyscallN(procAddDllDirectory.Addr(), uintptr(unsafe.Pointer(path))) cookie = uintptr(r0) if cookie == 0 { err = errnoErr(e1) @@ -1704,7 +1743,7 @@ func AddDllDirectory(path *uint16) (cookie uintptr, err error) { } func AssignProcessToJobObject(job Handle, process Handle) (err error) { - r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0) + r1, _, e1 := syscall.SyscallN(procAssignProcessToJobObject.Addr(), uintptr(job), uintptr(process)) if r1 == 0 { err = errnoErr(e1) } @@ -1712,7 +1751,7 @@ func AssignProcessToJobObject(job Handle, process Handle) (err error) { } func CancelIo(s Handle) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0) + r1, _, e1 := syscall.SyscallN(procCancelIo.Addr(), uintptr(s)) if r1 == 0 { err = errnoErr(e1) } @@ -1720,7 +1759,7 @@ func CancelIo(s Handle) (err error) { } func CancelIoEx(s Handle, o *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0) + r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(s), uintptr(unsafe.Pointer(o))) if r1 == 0 { err = errnoErr(e1) } @@ -1728,7 +1767,7 @@ func CancelIoEx(s Handle, o *Overlapped) (err error) { } func ClearCommBreak(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procClearCommBreak.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -1736,7 +1775,7 @@ func ClearCommBreak(handle Handle) (err error) { } func ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error) { - r1, _, e1 := syscall.Syscall(procClearCommError.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat))) + r1, _, e1 := syscall.SyscallN(procClearCommError.Addr(), uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat))) if r1 == 0 { err = errnoErr(e1) } @@ -1744,7 +1783,7 @@ func ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error } func CloseHandle(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procCloseHandle.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -1752,12 +1791,12 @@ func CloseHandle(handle Handle) (err error) { } func ClosePseudoConsole(console Handle) { - syscall.Syscall(procClosePseudoConsole.Addr(), 1, uintptr(console), 0, 0) + syscall.SyscallN(procClosePseudoConsole.Addr(), uintptr(console)) return } func ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(overlapped)), 0) + r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -1765,7 +1804,7 @@ func ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error) { } func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) { - r1, _, e1 := syscall.Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0) + r1, _, e1 := syscall.SyscallN(procCreateDirectoryW.Addr(), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa))) if r1 == 0 { err = errnoErr(e1) } @@ -1773,7 +1812,7 @@ func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) { } func CreateEventEx(eventAttrs *SecurityAttributes, name *uint16, flags uint32, desiredAccess uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCreateEventExW.Addr(), 4, uintptr(unsafe.Pointer(eventAttrs)), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(desiredAccess), 0, 0) + r0, _, e1 := syscall.SyscallN(procCreateEventExW.Addr(), uintptr(unsafe.Pointer(eventAttrs)), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(desiredAccess)) handle = Handle(r0) if handle == 0 || e1 == ERROR_ALREADY_EXISTS { err = errnoErr(e1) @@ -1782,7 +1821,7 @@ func CreateEventEx(eventAttrs *SecurityAttributes, name *uint16, flags uint32, d } func CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttrs)), uintptr(manualReset), uintptr(initialState), uintptr(unsafe.Pointer(name)), 0, 0) + r0, _, e1 := syscall.SyscallN(procCreateEventW.Addr(), uintptr(unsafe.Pointer(eventAttrs)), uintptr(manualReset), uintptr(initialState), uintptr(unsafe.Pointer(name))) handle = Handle(r0) if handle == 0 || e1 == ERROR_ALREADY_EXISTS { err = errnoErr(e1) @@ -1791,7 +1830,7 @@ func CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialStat } func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCreateFileMappingW.Addr(), 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name))) + r0, _, e1 := syscall.SyscallN(procCreateFileMappingW.Addr(), uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name))) handle = Handle(r0) if handle == 0 || e1 == ERROR_ALREADY_EXISTS { err = errnoErr(e1) @@ -1800,7 +1839,7 @@ func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxS } func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) + r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -1809,7 +1848,7 @@ func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes } func CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procCreateHardLinkW.Addr(), 3, uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(existingfilename)), uintptr(reserved)) + r1, _, e1 := syscall.SyscallN(procCreateHardLinkW.Addr(), uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(existingfilename)), uintptr(reserved)) if r1&0xff == 0 { err = errnoErr(e1) } @@ -1817,7 +1856,7 @@ func CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr } func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uintptr, threadcnt uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt), 0, 0) + r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1826,7 +1865,7 @@ func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uintptr, thr } func CreateJobObject(jobAttr *SecurityAttributes, name *uint16) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procCreateJobObjectW.Addr(), 2, uintptr(unsafe.Pointer(jobAttr)), uintptr(unsafe.Pointer(name)), 0) + r0, _, e1 := syscall.SyscallN(procCreateJobObjectW.Addr(), uintptr(unsafe.Pointer(jobAttr)), uintptr(unsafe.Pointer(name))) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -1835,7 +1874,7 @@ func CreateJobObject(jobAttr *SecurityAttributes, name *uint16) (handle Handle, } func CreateMutexEx(mutexAttrs *SecurityAttributes, name *uint16, flags uint32, desiredAccess uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCreateMutexExW.Addr(), 4, uintptr(unsafe.Pointer(mutexAttrs)), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(desiredAccess), 0, 0) + r0, _, e1 := syscall.SyscallN(procCreateMutexExW.Addr(), uintptr(unsafe.Pointer(mutexAttrs)), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(desiredAccess)) handle = Handle(r0) if handle == 0 || e1 == ERROR_ALREADY_EXISTS { err = errnoErr(e1) @@ -1848,7 +1887,7 @@ func CreateMutex(mutexAttrs *SecurityAttributes, initialOwner bool, name *uint16 if initialOwner { _p0 = 1 } - r0, _, e1 := syscall.Syscall(procCreateMutexW.Addr(), 3, uintptr(unsafe.Pointer(mutexAttrs)), uintptr(_p0), uintptr(unsafe.Pointer(name))) + r0, _, e1 := syscall.SyscallN(procCreateMutexW.Addr(), uintptr(unsafe.Pointer(mutexAttrs)), uintptr(_p0), uintptr(unsafe.Pointer(name))) handle = Handle(r0) if handle == 0 || e1 == ERROR_ALREADY_EXISTS { err = errnoErr(e1) @@ -1857,7 +1896,7 @@ func CreateMutex(mutexAttrs *SecurityAttributes, initialOwner bool, name *uint16 } func CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa))) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -1866,7 +1905,7 @@ func CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances u } func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0) + r1, _, e1 := syscall.SyscallN(procCreatePipe.Addr(), uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size)) if r1 == 0 { err = errnoErr(e1) } @@ -1878,7 +1917,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA if inheritHandles { _p0 = 1 } - r1, _, e1 := syscall.Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCreateProcessW.Addr(), uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo))) if r1 == 0 { err = errnoErr(e1) } @@ -1886,7 +1925,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA } func createPseudoConsole(size uint32, in Handle, out Handle, flags uint32, pconsole *Handle) (hr error) { - r0, _, _ := syscall.Syscall6(procCreatePseudoConsole.Addr(), 5, uintptr(size), uintptr(in), uintptr(out), uintptr(flags), uintptr(unsafe.Pointer(pconsole)), 0) + r0, _, _ := syscall.SyscallN(procCreatePseudoConsole.Addr(), uintptr(size), uintptr(in), uintptr(out), uintptr(flags), uintptr(unsafe.Pointer(pconsole))) if r0 != 0 { hr = syscall.Errno(r0) } @@ -1894,7 +1933,7 @@ func createPseudoConsole(size uint32, in Handle, out Handle, flags uint32, pcons } func CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procCreateSymbolicLinkW.Addr(), 3, uintptr(unsafe.Pointer(symlinkfilename)), uintptr(unsafe.Pointer(targetfilename)), uintptr(flags)) + r1, _, e1 := syscall.SyscallN(procCreateSymbolicLinkW.Addr(), uintptr(unsafe.Pointer(symlinkfilename)), uintptr(unsafe.Pointer(targetfilename)), uintptr(flags)) if r1&0xff == 0 { err = errnoErr(e1) } @@ -1902,7 +1941,7 @@ func CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags u } func CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procCreateToolhelp32Snapshot.Addr(), 2, uintptr(flags), uintptr(processId), 0) + r0, _, e1 := syscall.SyscallN(procCreateToolhelp32Snapshot.Addr(), uintptr(flags), uintptr(processId)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -1911,7 +1950,7 @@ func CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, er } func DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procDefineDosDeviceW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath))) + r1, _, e1 := syscall.SyscallN(procDefineDosDeviceW.Addr(), uintptr(flags), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath))) if r1 == 0 { err = errnoErr(e1) } @@ -1919,7 +1958,7 @@ func DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err } func DeleteFile(path *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + r1, _, e1 := syscall.SyscallN(procDeleteFileW.Addr(), uintptr(unsafe.Pointer(path))) if r1 == 0 { err = errnoErr(e1) } @@ -1927,12 +1966,12 @@ func DeleteFile(path *uint16) (err error) { } func deleteProcThreadAttributeList(attrlist *ProcThreadAttributeList) { - syscall.Syscall(procDeleteProcThreadAttributeList.Addr(), 1, uintptr(unsafe.Pointer(attrlist)), 0, 0) + syscall.SyscallN(procDeleteProcThreadAttributeList.Addr(), uintptr(unsafe.Pointer(attrlist))) return } func DeleteVolumeMountPoint(volumeMountPoint *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procDeleteVolumeMountPointW.Addr(), 1, uintptr(unsafe.Pointer(volumeMountPoint)), 0, 0) + r1, _, e1 := syscall.SyscallN(procDeleteVolumeMountPointW.Addr(), uintptr(unsafe.Pointer(volumeMountPoint))) if r1 == 0 { err = errnoErr(e1) } @@ -1940,7 +1979,7 @@ func DeleteVolumeMountPoint(volumeMountPoint *uint16) (err error) { } func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBufferSize uint32, outBuffer *byte, outBufferSize uint32, bytesReturned *uint32, overlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), uintptr(ioControlCode), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferSize), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferSize), uintptr(unsafe.Pointer(bytesReturned)), uintptr(unsafe.Pointer(overlapped)), 0) + r1, _, e1 := syscall.SyscallN(procDeviceIoControl.Addr(), uintptr(handle), uintptr(ioControlCode), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferSize), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferSize), uintptr(unsafe.Pointer(bytesReturned)), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -1948,7 +1987,7 @@ func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBuff } func DisconnectNamedPipe(pipe Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(pipe), 0, 0) + r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe)) if r1 == 0 { err = errnoErr(e1) } @@ -1960,7 +1999,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP if bInheritHandle { _p0 = 1 } - r1, _, e1 := syscall.Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0) + r1, _, e1 := syscall.SyscallN(procDuplicateHandle.Addr(), uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions)) if r1 == 0 { err = errnoErr(e1) } @@ -1968,7 +2007,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP } func EscapeCommFunction(handle Handle, dwFunc uint32) (err error) { - r1, _, e1 := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(dwFunc), 0) + r1, _, e1 := syscall.SyscallN(procEscapeCommFunction.Addr(), uintptr(handle), uintptr(dwFunc)) if r1 == 0 { err = errnoErr(e1) } @@ -1976,12 +2015,12 @@ func EscapeCommFunction(handle Handle, dwFunc uint32) (err error) { } func ExitProcess(exitcode uint32) { - syscall.Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0) + syscall.SyscallN(procExitProcess.Addr(), uintptr(exitcode)) return } func ExpandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) + r0, _, e1 := syscall.SyscallN(procExpandEnvironmentStringsW.Addr(), uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -1990,7 +2029,7 @@ func ExpandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, } func FindClose(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procFindClose.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -1998,7 +2037,7 @@ func FindClose(handle Handle) (err error) { } func FindCloseChangeNotification(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFindCloseChangeNotification.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procFindCloseChangeNotification.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -2019,7 +2058,7 @@ func _FindFirstChangeNotification(path *uint16, watchSubtree bool, notifyFilter if watchSubtree { _p1 = 1 } - r0, _, e1 := syscall.Syscall(procFindFirstChangeNotificationW.Addr(), 3, uintptr(unsafe.Pointer(path)), uintptr(_p1), uintptr(notifyFilter)) + r0, _, e1 := syscall.SyscallN(procFindFirstChangeNotificationW.Addr(), uintptr(unsafe.Pointer(path)), uintptr(_p1), uintptr(notifyFilter)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -2028,7 +2067,7 @@ func _FindFirstChangeNotification(path *uint16, watchSubtree bool, notifyFilter } func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procFindFirstFileW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0) + r0, _, e1 := syscall.SyscallN(procFindFirstFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data))) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -2037,7 +2076,7 @@ func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err erro } func FindFirstVolumeMountPoint(rootPathName *uint16, volumeMountPoint *uint16, bufferLength uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procFindFirstVolumeMountPointW.Addr(), 3, uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(bufferLength)) + r0, _, e1 := syscall.SyscallN(procFindFirstVolumeMountPointW.Addr(), uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(bufferLength)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -2046,7 +2085,7 @@ func FindFirstVolumeMountPoint(rootPathName *uint16, volumeMountPoint *uint16, b } func FindFirstVolume(volumeName *uint16, bufferLength uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procFindFirstVolumeW.Addr(), 2, uintptr(unsafe.Pointer(volumeName)), uintptr(bufferLength), 0) + r0, _, e1 := syscall.SyscallN(procFindFirstVolumeW.Addr(), uintptr(unsafe.Pointer(volumeName)), uintptr(bufferLength)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -2055,7 +2094,7 @@ func FindFirstVolume(volumeName *uint16, bufferLength uint32) (handle Handle, er } func FindNextChangeNotification(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFindNextChangeNotification.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procFindNextChangeNotification.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -2063,7 +2102,7 @@ func FindNextChangeNotification(handle Handle) (err error) { } func findNextFile1(handle Handle, data *win32finddata1) (err error) { - r1, _, e1 := syscall.Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) + r1, _, e1 := syscall.SyscallN(procFindNextFileW.Addr(), uintptr(handle), uintptr(unsafe.Pointer(data))) if r1 == 0 { err = errnoErr(e1) } @@ -2071,7 +2110,7 @@ func findNextFile1(handle Handle, data *win32finddata1) (err error) { } func FindNextVolumeMountPoint(findVolumeMountPoint Handle, volumeMountPoint *uint16, bufferLength uint32) (err error) { - r1, _, e1 := syscall.Syscall(procFindNextVolumeMountPointW.Addr(), 3, uintptr(findVolumeMountPoint), uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(bufferLength)) + r1, _, e1 := syscall.SyscallN(procFindNextVolumeMountPointW.Addr(), uintptr(findVolumeMountPoint), uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(bufferLength)) if r1 == 0 { err = errnoErr(e1) } @@ -2079,7 +2118,7 @@ func FindNextVolumeMountPoint(findVolumeMountPoint Handle, volumeMountPoint *uin } func FindNextVolume(findVolume Handle, volumeName *uint16, bufferLength uint32) (err error) { - r1, _, e1 := syscall.Syscall(procFindNextVolumeW.Addr(), 3, uintptr(findVolume), uintptr(unsafe.Pointer(volumeName)), uintptr(bufferLength)) + r1, _, e1 := syscall.SyscallN(procFindNextVolumeW.Addr(), uintptr(findVolume), uintptr(unsafe.Pointer(volumeName)), uintptr(bufferLength)) if r1 == 0 { err = errnoErr(e1) } @@ -2087,7 +2126,7 @@ func FindNextVolume(findVolume Handle, volumeName *uint16, bufferLength uint32) } func findResource(module Handle, name uintptr, resType uintptr) (resInfo Handle, err error) { - r0, _, e1 := syscall.Syscall(procFindResourceW.Addr(), 3, uintptr(module), uintptr(name), uintptr(resType)) + r0, _, e1 := syscall.SyscallN(procFindResourceW.Addr(), uintptr(module), uintptr(name), uintptr(resType)) resInfo = Handle(r0) if resInfo == 0 { err = errnoErr(e1) @@ -2096,7 +2135,7 @@ func findResource(module Handle, name uintptr, resType uintptr) (resInfo Handle, } func FindVolumeClose(findVolume Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFindVolumeClose.Addr(), 1, uintptr(findVolume), 0, 0) + r1, _, e1 := syscall.SyscallN(procFindVolumeClose.Addr(), uintptr(findVolume)) if r1 == 0 { err = errnoErr(e1) } @@ -2104,7 +2143,15 @@ func FindVolumeClose(findVolume Handle) (err error) { } func FindVolumeMountPointClose(findVolumeMountPoint Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFindVolumeMountPointClose.Addr(), 1, uintptr(findVolumeMountPoint), 0, 0) + r1, _, e1 := syscall.SyscallN(procFindVolumeMountPointClose.Addr(), uintptr(findVolumeMountPoint)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func FlushConsoleInputBuffer(console Handle) (err error) { + r1, _, e1 := syscall.SyscallN(procFlushConsoleInputBuffer.Addr(), uintptr(console)) if r1 == 0 { err = errnoErr(e1) } @@ -2112,7 +2159,7 @@ func FindVolumeMountPointClose(findVolumeMountPoint Handle) (err error) { } func FlushFileBuffers(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procFlushFileBuffers.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -2120,7 +2167,7 @@ func FlushFileBuffers(handle Handle) (err error) { } func FlushViewOfFile(addr uintptr, length uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0) + r1, _, e1 := syscall.SyscallN(procFlushViewOfFile.Addr(), uintptr(addr), uintptr(length)) if r1 == 0 { err = errnoErr(e1) } @@ -2132,7 +2179,7 @@ func FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, bu if len(buf) > 0 { _p0 = &buf[0] } - r0, _, e1 := syscall.Syscall9(procFormatMessageW.Addr(), 7, uintptr(flags), uintptr(msgsrc), uintptr(msgid), uintptr(langid), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(args)), 0, 0) + r0, _, e1 := syscall.SyscallN(procFormatMessageW.Addr(), uintptr(flags), uintptr(msgsrc), uintptr(msgid), uintptr(langid), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(args))) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2141,7 +2188,7 @@ func FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, bu } func FreeEnvironmentStrings(envs *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0) + r1, _, e1 := syscall.SyscallN(procFreeEnvironmentStringsW.Addr(), uintptr(unsafe.Pointer(envs))) if r1 == 0 { err = errnoErr(e1) } @@ -2149,7 +2196,7 @@ func FreeEnvironmentStrings(envs *uint16) (err error) { } func FreeLibrary(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procFreeLibrary.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -2157,7 +2204,7 @@ func FreeLibrary(handle Handle) (err error) { } func GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGenerateConsoleCtrlEvent.Addr(), 2, uintptr(ctrlEvent), uintptr(processGroupID), 0) + r1, _, e1 := syscall.SyscallN(procGenerateConsoleCtrlEvent.Addr(), uintptr(ctrlEvent), uintptr(processGroupID)) if r1 == 0 { err = errnoErr(e1) } @@ -2165,19 +2212,19 @@ func GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err erro } func GetACP() (acp uint32) { - r0, _, _ := syscall.Syscall(procGetACP.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetACP.Addr()) acp = uint32(r0) return } func GetActiveProcessorCount(groupNumber uint16) (ret uint32) { - r0, _, _ := syscall.Syscall(procGetActiveProcessorCount.Addr(), 1, uintptr(groupNumber), 0, 0) + r0, _, _ := syscall.SyscallN(procGetActiveProcessorCount.Addr(), uintptr(groupNumber)) ret = uint32(r0) return } func GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpModemStat)), 0) + r1, _, e1 := syscall.SyscallN(procGetCommModemStatus.Addr(), uintptr(handle), uintptr(unsafe.Pointer(lpModemStat))) if r1 == 0 { err = errnoErr(e1) } @@ -2185,7 +2232,7 @@ func GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) { } func GetCommState(handle Handle, lpDCB *DCB) (err error) { - r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0) + r1, _, e1 := syscall.SyscallN(procGetCommState.Addr(), uintptr(handle), uintptr(unsafe.Pointer(lpDCB))) if r1 == 0 { err = errnoErr(e1) } @@ -2193,7 +2240,7 @@ func GetCommState(handle Handle, lpDCB *DCB) (err error) { } func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { - r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) + r1, _, e1 := syscall.SyscallN(procGetCommTimeouts.Addr(), uintptr(handle), uintptr(unsafe.Pointer(timeouts))) if r1 == 0 { err = errnoErr(e1) } @@ -2201,13 +2248,13 @@ func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { } func GetCommandLine() (cmd *uint16) { - r0, _, _ := syscall.Syscall(procGetCommandLineW.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetCommandLineW.Addr()) cmd = (*uint16)(unsafe.Pointer(r0)) return } func GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nametype), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) + r1, _, e1 := syscall.SyscallN(procGetComputerNameExW.Addr(), uintptr(nametype), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) if r1 == 0 { err = errnoErr(e1) } @@ -2215,7 +2262,7 @@ func GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) { } func GetComputerName(buf *uint16, n *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) + r1, _, e1 := syscall.SyscallN(procGetComputerNameW.Addr(), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) if r1 == 0 { err = errnoErr(e1) } @@ -2223,7 +2270,7 @@ func GetComputerName(buf *uint16, n *uint32) (err error) { } func GetConsoleCP() (cp uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0) + r0, _, e1 := syscall.SyscallN(procGetConsoleCP.Addr()) cp = uint32(r0) if cp == 0 { err = errnoErr(e1) @@ -2232,7 +2279,7 @@ func GetConsoleCP() (cp uint32, err error) { } func GetConsoleMode(console Handle, mode *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0) + r1, _, e1 := syscall.SyscallN(procGetConsoleMode.Addr(), uintptr(console), uintptr(unsafe.Pointer(mode))) if r1 == 0 { err = errnoErr(e1) } @@ -2240,7 +2287,7 @@ func GetConsoleMode(console Handle, mode *uint32) (err error) { } func GetConsoleOutputCP() (cp uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetConsoleOutputCP.Addr(), 0, 0, 0, 0) + r0, _, e1 := syscall.SyscallN(procGetConsoleOutputCP.Addr()) cp = uint32(r0) if cp == 0 { err = errnoErr(e1) @@ -2249,7 +2296,7 @@ func GetConsoleOutputCP() (cp uint32, err error) { } func GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) { - r1, _, e1 := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(info)), 0) + r1, _, e1 := syscall.SyscallN(procGetConsoleScreenBufferInfo.Addr(), uintptr(console), uintptr(unsafe.Pointer(info))) if r1 == 0 { err = errnoErr(e1) } @@ -2257,7 +2304,7 @@ func GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) ( } func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetCurrentDirectoryW.Addr(), 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) + r0, _, e1 := syscall.SyscallN(procGetCurrentDirectoryW.Addr(), uintptr(buflen), uintptr(unsafe.Pointer(buf))) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2266,19 +2313,19 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) { } func GetCurrentProcessId() (pid uint32) { - r0, _, _ := syscall.Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetCurrentProcessId.Addr()) pid = uint32(r0) return } func GetCurrentThreadId() (id uint32) { - r0, _, _ := syscall.Syscall(procGetCurrentThreadId.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetCurrentThreadId.Addr()) id = uint32(r0) return } func GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) { - r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceExW.Addr(), 4, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailableToCaller)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetDiskFreeSpaceExW.Addr(), uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailableToCaller)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes))) if r1 == 0 { err = errnoErr(e1) } @@ -2286,13 +2333,13 @@ func GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *uint6 } func GetDriveType(rootPathName *uint16) (driveType uint32) { - r0, _, _ := syscall.Syscall(procGetDriveTypeW.Addr(), 1, uintptr(unsafe.Pointer(rootPathName)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetDriveTypeW.Addr(), uintptr(unsafe.Pointer(rootPathName))) driveType = uint32(r0) return } func GetEnvironmentStrings() (envs *uint16, err error) { - r0, _, e1 := syscall.Syscall(procGetEnvironmentStringsW.Addr(), 0, 0, 0, 0) + r0, _, e1 := syscall.SyscallN(procGetEnvironmentStringsW.Addr()) envs = (*uint16)(unsafe.Pointer(r0)) if envs == nil { err = errnoErr(e1) @@ -2301,7 +2348,7 @@ func GetEnvironmentStrings() (envs *uint16, err error) { } func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetEnvironmentVariableW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(size)) + r0, _, e1 := syscall.SyscallN(procGetEnvironmentVariableW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(size)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2310,7 +2357,7 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32 } func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) + r1, _, e1 := syscall.SyscallN(procGetExitCodeProcess.Addr(), uintptr(handle), uintptr(unsafe.Pointer(exitcode))) if r1 == 0 { err = errnoErr(e1) } @@ -2318,7 +2365,7 @@ func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) { } func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) { - r1, _, e1 := syscall.Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info))) + r1, _, e1 := syscall.SyscallN(procGetFileAttributesExW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info))) if r1 == 0 { err = errnoErr(e1) } @@ -2326,7 +2373,7 @@ func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) { } func GetFileAttributes(name *uint16) (attrs uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetFileAttributesW.Addr(), 1, uintptr(unsafe.Pointer(name)), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetFileAttributesW.Addr(), uintptr(unsafe.Pointer(name))) attrs = uint32(r0) if attrs == INVALID_FILE_ATTRIBUTES { err = errnoErr(e1) @@ -2335,7 +2382,7 @@ func GetFileAttributes(name *uint16) (attrs uint32, err error) { } func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) { - r1, _, e1 := syscall.Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) + r1, _, e1 := syscall.SyscallN(procGetFileInformationByHandle.Addr(), uintptr(handle), uintptr(unsafe.Pointer(data))) if r1 == 0 { err = errnoErr(e1) } @@ -2343,7 +2390,7 @@ func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (e } func GetFileInformationByHandleEx(handle Handle, class uint32, outBuffer *byte, outBufferLen uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferLen), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetFileInformationByHandleEx.Addr(), uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferLen)) if r1 == 0 { err = errnoErr(e1) } @@ -2351,7 +2398,7 @@ func GetFileInformationByHandleEx(handle Handle, class uint32, outBuffer *byte, } func GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { - r1, _, e1 := syscall.Syscall6(procGetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetFileTime.Addr(), uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime))) if r1 == 0 { err = errnoErr(e1) } @@ -2359,7 +2406,7 @@ func GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim } func GetFileType(filehandle Handle) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetFileType.Addr(), uintptr(filehandle)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2368,7 +2415,7 @@ func GetFileType(filehandle Handle) (n uint32, err error) { } func GetFinalPathNameByHandle(file Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall6(procGetFinalPathNameByHandleW.Addr(), 4, uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetFinalPathNameByHandleW.Addr(), uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2377,7 +2424,7 @@ func GetFinalPathNameByHandle(file Handle, filePath *uint16, filePathSize uint32 } func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, err error) { - r0, _, e1 := syscall.Syscall6(procGetFullPathNameW.Addr(), 4, uintptr(unsafe.Pointer(path)), uintptr(buflen), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(fname)), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetFullPathNameW.Addr(), uintptr(unsafe.Pointer(path)), uintptr(buflen), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(fname))) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2386,13 +2433,13 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) ( } func GetLargePageMinimum() (size uintptr) { - r0, _, _ := syscall.Syscall(procGetLargePageMinimum.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetLargePageMinimum.Addr()) size = uintptr(r0) return } func GetLastError() (lasterr error) { - r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetLastError.Addr()) if r0 != 0 { lasterr = syscall.Errno(r0) } @@ -2400,7 +2447,7 @@ func GetLastError() (lasterr error) { } func GetLogicalDriveStrings(bufferLength uint32, buffer *uint16) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetLogicalDriveStringsW.Addr(), 2, uintptr(bufferLength), uintptr(unsafe.Pointer(buffer)), 0) + r0, _, e1 := syscall.SyscallN(procGetLogicalDriveStringsW.Addr(), uintptr(bufferLength), uintptr(unsafe.Pointer(buffer))) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2409,7 +2456,7 @@ func GetLogicalDriveStrings(bufferLength uint32, buffer *uint16) (n uint32, err } func GetLogicalDrives() (drivesBitMask uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetLogicalDrives.Addr(), 0, 0, 0, 0) + r0, _, e1 := syscall.SyscallN(procGetLogicalDrives.Addr()) drivesBitMask = uint32(r0) if drivesBitMask == 0 { err = errnoErr(e1) @@ -2418,7 +2465,7 @@ func GetLogicalDrives() (drivesBitMask uint32, err error) { } func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetLongPathNameW.Addr(), 3, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(buf)), uintptr(buflen)) + r0, _, e1 := syscall.SyscallN(procGetLongPathNameW.Addr(), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(buf)), uintptr(buflen)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2427,13 +2474,13 @@ func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err er } func GetMaximumProcessorCount(groupNumber uint16) (ret uint32) { - r0, _, _ := syscall.Syscall(procGetMaximumProcessorCount.Addr(), 1, uintptr(groupNumber), 0, 0) + r0, _, _ := syscall.SyscallN(procGetMaximumProcessorCount.Addr(), uintptr(groupNumber)) ret = uint32(r0) return } func GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size)) + r0, _, e1 := syscall.SyscallN(procGetModuleFileNameW.Addr(), uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2442,7 +2489,7 @@ func GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, } func GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) { - r1, _, e1 := syscall.Syscall(procGetModuleHandleExW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(moduleName)), uintptr(unsafe.Pointer(module))) + r1, _, e1 := syscall.SyscallN(procGetModuleHandleExW.Addr(), uintptr(flags), uintptr(unsafe.Pointer(moduleName)), uintptr(unsafe.Pointer(module))) if r1 == 0 { err = errnoErr(e1) } @@ -2450,7 +2497,7 @@ func GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err er } func GetNamedPipeClientProcessId(pipe Handle, clientProcessID *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetNamedPipeClientProcessId.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(clientProcessID)), 0) + r1, _, e1 := syscall.SyscallN(procGetNamedPipeClientProcessId.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(clientProcessID))) if r1 == 0 { err = errnoErr(e1) } @@ -2458,7 +2505,7 @@ func GetNamedPipeClientProcessId(pipe Handle, clientProcessID *uint32) (err erro } func GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize)) if r1 == 0 { err = errnoErr(e1) } @@ -2466,7 +2513,7 @@ func GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, m } func GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) + r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances))) if r1 == 0 { err = errnoErr(e1) } @@ -2474,7 +2521,15 @@ func GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint3 } func GetNamedPipeServerProcessId(pipe Handle, serverProcessID *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetNamedPipeServerProcessId.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(serverProcessID)), 0) + r1, _, e1 := syscall.SyscallN(procGetNamedPipeServerProcessId.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(serverProcessID))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func GetNumberOfConsoleInputEvents(console Handle, numevents *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procGetNumberOfConsoleInputEvents.Addr(), uintptr(console), uintptr(unsafe.Pointer(numevents))) if r1 == 0 { err = errnoErr(e1) } @@ -2486,7 +2541,7 @@ func GetOverlappedResult(handle Handle, overlapped *Overlapped, done *uint32, wa if wait { _p0 = 1 } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(done)), uintptr(_p0), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetOverlappedResult.Addr(), uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(done)), uintptr(_p0)) if r1 == 0 { err = errnoErr(e1) } @@ -2494,7 +2549,7 @@ func GetOverlappedResult(handle Handle, overlapped *Overlapped, done *uint32, wa } func GetPriorityClass(process Handle) (ret uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetPriorityClass.Addr(), 1, uintptr(process), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetPriorityClass.Addr(), uintptr(process)) ret = uint32(r0) if ret == 0 { err = errnoErr(e1) @@ -2512,7 +2567,7 @@ func GetProcAddress(module Handle, procname string) (proc uintptr, err error) { } func _GetProcAddress(module Handle, procname *byte) (proc uintptr, err error) { - r0, _, e1 := syscall.Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(procname)), 0) + r0, _, e1 := syscall.SyscallN(procGetProcAddress.Addr(), uintptr(module), uintptr(unsafe.Pointer(procname))) proc = uintptr(r0) if proc == 0 { err = errnoErr(e1) @@ -2521,7 +2576,7 @@ func _GetProcAddress(module Handle, procname *byte) (proc uintptr, err error) { } func GetProcessId(process Handle) (id uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetProcessId.Addr(), 1, uintptr(process), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetProcessId.Addr(), uintptr(process)) id = uint32(r0) if id == 0 { err = errnoErr(e1) @@ -2530,7 +2585,7 @@ func GetProcessId(process Handle) (id uint32, err error) { } func getProcessPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetProcessPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetProcessPreferredUILanguages.Addr(), uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize))) if r1 == 0 { err = errnoErr(e1) } @@ -2538,7 +2593,7 @@ func getProcessPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uin } func GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetProcessShutdownParameters.Addr(), 2, uintptr(unsafe.Pointer(level)), uintptr(unsafe.Pointer(flags)), 0) + r1, _, e1 := syscall.SyscallN(procGetProcessShutdownParameters.Addr(), uintptr(unsafe.Pointer(level)), uintptr(unsafe.Pointer(flags))) if r1 == 0 { err = errnoErr(e1) } @@ -2546,7 +2601,7 @@ func GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) { } func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) { - r1, _, e1 := syscall.Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0) + r1, _, e1 := syscall.SyscallN(procGetProcessTimes.Addr(), uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime))) if r1 == 0 { err = errnoErr(e1) } @@ -2554,12 +2609,12 @@ func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, } func GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32) { - syscall.Syscall6(procGetProcessWorkingSetSizeEx.Addr(), 4, uintptr(hProcess), uintptr(unsafe.Pointer(lpMinimumWorkingSetSize)), uintptr(unsafe.Pointer(lpMaximumWorkingSetSize)), uintptr(unsafe.Pointer(flags)), 0, 0) + syscall.SyscallN(procGetProcessWorkingSetSizeEx.Addr(), uintptr(hProcess), uintptr(unsafe.Pointer(lpMinimumWorkingSetSize)), uintptr(unsafe.Pointer(lpMaximumWorkingSetSize)), uintptr(unsafe.Pointer(flags))) return } func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uintptr, overlapped **Overlapped, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) + r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout)) if r1 == 0 { err = errnoErr(e1) } @@ -2567,7 +2622,7 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uintptr, overl } func GetShortPathName(longpath *uint16, shortpath *uint16, buflen uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetShortPathNameW.Addr(), 3, uintptr(unsafe.Pointer(longpath)), uintptr(unsafe.Pointer(shortpath)), uintptr(buflen)) + r0, _, e1 := syscall.SyscallN(procGetShortPathNameW.Addr(), uintptr(unsafe.Pointer(longpath)), uintptr(unsafe.Pointer(shortpath)), uintptr(buflen)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2576,12 +2631,12 @@ func GetShortPathName(longpath *uint16, shortpath *uint16, buflen uint32) (n uin } func getStartupInfo(startupInfo *StartupInfo) { - syscall.Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0) + syscall.SyscallN(procGetStartupInfoW.Addr(), uintptr(unsafe.Pointer(startupInfo))) return } func GetStdHandle(stdhandle uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procGetStdHandle.Addr(), 1, uintptr(stdhandle), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetStdHandle.Addr(), uintptr(stdhandle)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -2590,7 +2645,7 @@ func GetStdHandle(stdhandle uint32) (handle Handle, err error) { } func getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetSystemDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0) + r0, _, e1 := syscall.SyscallN(procGetSystemDirectoryW.Addr(), uintptr(unsafe.Pointer(dir)), uintptr(dirLen)) len = uint32(r0) if len == 0 { err = errnoErr(e1) @@ -2599,7 +2654,7 @@ func getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { } func getSystemPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetSystemPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetSystemPreferredUILanguages.Addr(), uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize))) if r1 == 0 { err = errnoErr(e1) } @@ -2607,17 +2662,17 @@ func getSystemPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint } func GetSystemTimeAsFileTime(time *Filetime) { - syscall.Syscall(procGetSystemTimeAsFileTime.Addr(), 1, uintptr(unsafe.Pointer(time)), 0, 0) + syscall.SyscallN(procGetSystemTimeAsFileTime.Addr(), uintptr(unsafe.Pointer(time))) return } func GetSystemTimePreciseAsFileTime(time *Filetime) { - syscall.Syscall(procGetSystemTimePreciseAsFileTime.Addr(), 1, uintptr(unsafe.Pointer(time)), 0, 0) + syscall.SyscallN(procGetSystemTimePreciseAsFileTime.Addr(), uintptr(unsafe.Pointer(time))) return } func getSystemWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetSystemWindowsDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0) + r0, _, e1 := syscall.SyscallN(procGetSystemWindowsDirectoryW.Addr(), uintptr(unsafe.Pointer(dir)), uintptr(dirLen)) len = uint32(r0) if len == 0 { err = errnoErr(e1) @@ -2626,7 +2681,7 @@ func getSystemWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err erro } func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetTempPathW.Addr(), 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) + r0, _, e1 := syscall.SyscallN(procGetTempPathW.Addr(), uintptr(buflen), uintptr(unsafe.Pointer(buf))) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2635,7 +2690,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) { } func getThreadPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetThreadPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetThreadPreferredUILanguages.Addr(), uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize))) if r1 == 0 { err = errnoErr(e1) } @@ -2643,13 +2698,13 @@ func getThreadPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint } func getTickCount64() (ms uint64) { - r0, _, _ := syscall.Syscall(procGetTickCount64.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetTickCount64.Addr()) ms = uint64(r0) return } func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetTimeZoneInformation.Addr(), 1, uintptr(unsafe.Pointer(tzi)), 0, 0) + r0, _, e1 := syscall.SyscallN(procGetTimeZoneInformation.Addr(), uintptr(unsafe.Pointer(tzi))) rc = uint32(r0) if rc == 0xffffffff { err = errnoErr(e1) @@ -2658,7 +2713,7 @@ func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) { } func getUserPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetUserPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetUserPreferredUILanguages.Addr(), uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize))) if r1 == 0 { err = errnoErr(e1) } @@ -2666,7 +2721,7 @@ func getUserPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16 } func GetVersion() (ver uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetVersion.Addr(), 0, 0, 0, 0) + r0, _, e1 := syscall.SyscallN(procGetVersion.Addr()) ver = uint32(r0) if ver == 0 { err = errnoErr(e1) @@ -2675,7 +2730,7 @@ func GetVersion() (ver uint32, err error) { } func GetVolumeInformationByHandle(file Handle, volumeNameBuffer *uint16, volumeNameSize uint32, volumeNameSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemNameBuffer *uint16, fileSystemNameSize uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procGetVolumeInformationByHandleW.Addr(), 8, uintptr(file), uintptr(unsafe.Pointer(volumeNameBuffer)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeNameSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemNameBuffer)), uintptr(fileSystemNameSize), 0) + r1, _, e1 := syscall.SyscallN(procGetVolumeInformationByHandleW.Addr(), uintptr(file), uintptr(unsafe.Pointer(volumeNameBuffer)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeNameSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemNameBuffer)), uintptr(fileSystemNameSize)) if r1 == 0 { err = errnoErr(e1) } @@ -2683,7 +2738,7 @@ func GetVolumeInformationByHandle(file Handle, volumeNameBuffer *uint16, volumeN } func GetVolumeInformation(rootPathName *uint16, volumeNameBuffer *uint16, volumeNameSize uint32, volumeNameSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemNameBuffer *uint16, fileSystemNameSize uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procGetVolumeInformationW.Addr(), 8, uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeNameBuffer)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeNameSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemNameBuffer)), uintptr(fileSystemNameSize), 0) + r1, _, e1 := syscall.SyscallN(procGetVolumeInformationW.Addr(), uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeNameBuffer)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeNameSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemNameBuffer)), uintptr(fileSystemNameSize)) if r1 == 0 { err = errnoErr(e1) } @@ -2691,7 +2746,7 @@ func GetVolumeInformation(rootPathName *uint16, volumeNameBuffer *uint16, volume } func GetVolumeNameForVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16, bufferlength uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetVolumeNameForVolumeMountPointW.Addr(), 3, uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(unsafe.Pointer(volumeName)), uintptr(bufferlength)) + r1, _, e1 := syscall.SyscallN(procGetVolumeNameForVolumeMountPointW.Addr(), uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(unsafe.Pointer(volumeName)), uintptr(bufferlength)) if r1 == 0 { err = errnoErr(e1) } @@ -2699,7 +2754,7 @@ func GetVolumeNameForVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint } func GetVolumePathName(fileName *uint16, volumePathName *uint16, bufferLength uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetVolumePathNameW.Addr(), 3, uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(volumePathName)), uintptr(bufferLength)) + r1, _, e1 := syscall.SyscallN(procGetVolumePathNameW.Addr(), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(volumePathName)), uintptr(bufferLength)) if r1 == 0 { err = errnoErr(e1) } @@ -2707,7 +2762,7 @@ func GetVolumePathName(fileName *uint16, volumePathName *uint16, bufferLength ui } func GetVolumePathNamesForVolumeName(volumeName *uint16, volumePathNames *uint16, bufferLength uint32, returnLength *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetVolumePathNamesForVolumeNameW.Addr(), 4, uintptr(unsafe.Pointer(volumeName)), uintptr(unsafe.Pointer(volumePathNames)), uintptr(bufferLength), uintptr(unsafe.Pointer(returnLength)), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetVolumePathNamesForVolumeNameW.Addr(), uintptr(unsafe.Pointer(volumeName)), uintptr(unsafe.Pointer(volumePathNames)), uintptr(bufferLength), uintptr(unsafe.Pointer(returnLength))) if r1 == 0 { err = errnoErr(e1) } @@ -2715,7 +2770,7 @@ func GetVolumePathNamesForVolumeName(volumeName *uint16, volumePathNames *uint16 } func getWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetWindowsDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0) + r0, _, e1 := syscall.SyscallN(procGetWindowsDirectoryW.Addr(), uintptr(unsafe.Pointer(dir)), uintptr(dirLen)) len = uint32(r0) if len == 0 { err = errnoErr(e1) @@ -2724,7 +2779,7 @@ func getWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { } func initializeProcThreadAttributeList(attrlist *ProcThreadAttributeList, attrcount uint32, flags uint32, size *uintptr) (err error) { - r1, _, e1 := syscall.Syscall6(procInitializeProcThreadAttributeList.Addr(), 4, uintptr(unsafe.Pointer(attrlist)), uintptr(attrcount), uintptr(flags), uintptr(unsafe.Pointer(size)), 0, 0) + r1, _, e1 := syscall.SyscallN(procInitializeProcThreadAttributeList.Addr(), uintptr(unsafe.Pointer(attrlist)), uintptr(attrcount), uintptr(flags), uintptr(unsafe.Pointer(size))) if r1 == 0 { err = errnoErr(e1) } @@ -2736,7 +2791,7 @@ func IsWow64Process(handle Handle, isWow64 *bool) (err error) { if *isWow64 { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procIsWow64Process.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(&_p0)), 0) + r1, _, e1 := syscall.SyscallN(procIsWow64Process.Addr(), uintptr(handle), uintptr(unsafe.Pointer(&_p0))) *isWow64 = _p0 != 0 if r1 == 0 { err = errnoErr(e1) @@ -2749,7 +2804,7 @@ func IsWow64Process2(handle Handle, processMachine *uint16, nativeMachine *uint1 if err != nil { return } - r1, _, e1 := syscall.Syscall(procIsWow64Process2.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(processMachine)), uintptr(unsafe.Pointer(nativeMachine))) + r1, _, e1 := syscall.SyscallN(procIsWow64Process2.Addr(), uintptr(handle), uintptr(unsafe.Pointer(processMachine)), uintptr(unsafe.Pointer(nativeMachine))) if r1 == 0 { err = errnoErr(e1) } @@ -2766,7 +2821,7 @@ func LoadLibraryEx(libname string, zero Handle, flags uintptr) (handle Handle, e } func _LoadLibraryEx(libname *uint16, zero Handle, flags uintptr) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procLoadLibraryExW.Addr(), 3, uintptr(unsafe.Pointer(libname)), uintptr(zero), uintptr(flags)) + r0, _, e1 := syscall.SyscallN(procLoadLibraryExW.Addr(), uintptr(unsafe.Pointer(libname)), uintptr(zero), uintptr(flags)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -2784,7 +2839,7 @@ func LoadLibrary(libname string) (handle Handle, err error) { } func _LoadLibrary(libname *uint16) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(libname)), 0, 0) + r0, _, e1 := syscall.SyscallN(procLoadLibraryW.Addr(), uintptr(unsafe.Pointer(libname))) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -2793,7 +2848,7 @@ func _LoadLibrary(libname *uint16) (handle Handle, err error) { } func LoadResource(module Handle, resInfo Handle) (resData Handle, err error) { - r0, _, e1 := syscall.Syscall(procLoadResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0) + r0, _, e1 := syscall.SyscallN(procLoadResource.Addr(), uintptr(module), uintptr(resInfo)) resData = Handle(r0) if resData == 0 { err = errnoErr(e1) @@ -2802,7 +2857,7 @@ func LoadResource(module Handle, resInfo Handle) (resData Handle, err error) { } func LocalAlloc(flags uint32, length uint32) (ptr uintptr, err error) { - r0, _, e1 := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(length), 0) + r0, _, e1 := syscall.SyscallN(procLocalAlloc.Addr(), uintptr(flags), uintptr(length)) ptr = uintptr(r0) if ptr == 0 { err = errnoErr(e1) @@ -2811,7 +2866,7 @@ func LocalAlloc(flags uint32, length uint32) (ptr uintptr, err error) { } func LocalFree(hmem Handle) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procLocalFree.Addr(), 1, uintptr(hmem), 0, 0) + r0, _, e1 := syscall.SyscallN(procLocalFree.Addr(), uintptr(hmem)) handle = Handle(r0) if handle != 0 { err = errnoErr(e1) @@ -2820,7 +2875,7 @@ func LocalFree(hmem Handle) (handle Handle, err error) { } func LockFileEx(file Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) + r1, _, e1 := syscall.SyscallN(procLockFileEx.Addr(), uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -2828,7 +2883,7 @@ func LockFileEx(file Handle, flags uint32, reserved uint32, bytesLow uint32, byt } func LockResource(resData Handle) (addr uintptr, err error) { - r0, _, e1 := syscall.Syscall(procLockResource.Addr(), 1, uintptr(resData), 0, 0) + r0, _, e1 := syscall.SyscallN(procLockResource.Addr(), uintptr(resData)) addr = uintptr(r0) if addr == 0 { err = errnoErr(e1) @@ -2837,7 +2892,7 @@ func LockResource(resData Handle) (addr uintptr, err error) { } func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, err error) { - r0, _, e1 := syscall.Syscall6(procMapViewOfFile.Addr(), 5, uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length), 0) + r0, _, e1 := syscall.SyscallN(procMapViewOfFile.Addr(), uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length)) addr = uintptr(r0) if addr == 0 { err = errnoErr(e1) @@ -2846,7 +2901,7 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui } func Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) { - r1, _, e1 := syscall.Syscall(procModule32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0) + r1, _, e1 := syscall.SyscallN(procModule32FirstW.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry))) if r1 == 0 { err = errnoErr(e1) } @@ -2854,7 +2909,7 @@ func Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) { } func Module32Next(snapshot Handle, moduleEntry *ModuleEntry32) (err error) { - r1, _, e1 := syscall.Syscall(procModule32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0) + r1, _, e1 := syscall.SyscallN(procModule32NextW.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry))) if r1 == 0 { err = errnoErr(e1) } @@ -2862,7 +2917,7 @@ func Module32Next(snapshot Handle, moduleEntry *ModuleEntry32) (err error) { } func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags)) + r1, _, e1 := syscall.SyscallN(procMoveFileExW.Addr(), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -2870,7 +2925,7 @@ func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) { } func MoveFile(from *uint16, to *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0) + r1, _, e1 := syscall.SyscallN(procMoveFileW.Addr(), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to))) if r1 == 0 { err = errnoErr(e1) } @@ -2878,7 +2933,7 @@ func MoveFile(from *uint16, to *uint16) (err error) { } func MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) { - r0, _, e1 := syscall.Syscall6(procMultiByteToWideChar.Addr(), 6, uintptr(codePage), uintptr(dwFlags), uintptr(unsafe.Pointer(str)), uintptr(nstr), uintptr(unsafe.Pointer(wchar)), uintptr(nwchar)) + r0, _, e1 := syscall.SyscallN(procMultiByteToWideChar.Addr(), uintptr(codePage), uintptr(dwFlags), uintptr(unsafe.Pointer(str)), uintptr(nstr), uintptr(unsafe.Pointer(wchar)), uintptr(nwchar)) nwrite = int32(r0) if nwrite == 0 { err = errnoErr(e1) @@ -2891,7 +2946,7 @@ func OpenEvent(desiredAccess uint32, inheritHandle bool, name *uint16) (handle H if inheritHandle { _p0 = 1 } - r0, _, e1 := syscall.Syscall(procOpenEventW.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(name))) + r0, _, e1 := syscall.SyscallN(procOpenEventW.Addr(), uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(name))) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -2904,7 +2959,7 @@ func OpenMutex(desiredAccess uint32, inheritHandle bool, name *uint16) (handle H if inheritHandle { _p0 = 1 } - r0, _, e1 := syscall.Syscall(procOpenMutexW.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(name))) + r0, _, e1 := syscall.SyscallN(procOpenMutexW.Addr(), uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(name))) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -2917,7 +2972,7 @@ func OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (ha if inheritHandle { _p0 = 1 } - r0, _, e1 := syscall.Syscall(procOpenProcess.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(processId)) + r0, _, e1 := syscall.SyscallN(procOpenProcess.Addr(), uintptr(desiredAccess), uintptr(_p0), uintptr(processId)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -2930,7 +2985,7 @@ func OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (hand if inheritHandle { _p0 = 1 } - r0, _, e1 := syscall.Syscall(procOpenThread.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(threadId)) + r0, _, e1 := syscall.SyscallN(procOpenThread.Addr(), uintptr(desiredAccess), uintptr(_p0), uintptr(threadId)) handle = Handle(r0) if handle == 0 { err = errnoErr(e1) @@ -2939,7 +2994,7 @@ func OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (hand } func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uintptr, overlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0) + r1, _, e1 := syscall.SyscallN(procPostQueuedCompletionStatus.Addr(), uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -2947,7 +3002,7 @@ func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uintptr, overla } func Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) { - r1, _, e1 := syscall.Syscall(procProcess32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)), 0) + r1, _, e1 := syscall.SyscallN(procProcess32FirstW.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(procEntry))) if r1 == 0 { err = errnoErr(e1) } @@ -2955,7 +3010,7 @@ func Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) { } func Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) { - r1, _, e1 := syscall.Syscall(procProcess32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)), 0) + r1, _, e1 := syscall.SyscallN(procProcess32NextW.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(procEntry))) if r1 == 0 { err = errnoErr(e1) } @@ -2963,7 +3018,7 @@ func Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) { } func ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procProcessIdToSessionId.Addr(), 2, uintptr(pid), uintptr(unsafe.Pointer(sessionid)), 0) + r1, _, e1 := syscall.SyscallN(procProcessIdToSessionId.Addr(), uintptr(pid), uintptr(unsafe.Pointer(sessionid))) if r1 == 0 { err = errnoErr(e1) } @@ -2971,7 +3026,7 @@ func ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) { } func PulseEvent(event Handle) (err error) { - r1, _, e1 := syscall.Syscall(procPulseEvent.Addr(), 1, uintptr(event), 0, 0) + r1, _, e1 := syscall.SyscallN(procPulseEvent.Addr(), uintptr(event)) if r1 == 0 { err = errnoErr(e1) } @@ -2979,7 +3034,7 @@ func PulseEvent(event Handle) (err error) { } func PurgeComm(handle Handle, dwFlags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(dwFlags), 0) + r1, _, e1 := syscall.SyscallN(procPurgeComm.Addr(), uintptr(handle), uintptr(dwFlags)) if r1 == 0 { err = errnoErr(e1) } @@ -2987,7 +3042,7 @@ func PurgeComm(handle Handle, dwFlags uint32) (err error) { } func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint32, err error) { - r0, _, e1 := syscall.Syscall(procQueryDosDeviceW.Addr(), 3, uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max)) + r0, _, e1 := syscall.SyscallN(procQueryDosDeviceW.Addr(), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max)) n = uint32(r0) if n == 0 { err = errnoErr(e1) @@ -2996,7 +3051,7 @@ func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint3 } func QueryFullProcessImageName(proc Handle, flags uint32, exeName *uint16, size *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryFullProcessImageNameW.Addr(), 4, uintptr(proc), uintptr(flags), uintptr(unsafe.Pointer(exeName)), uintptr(unsafe.Pointer(size)), 0, 0) + r1, _, e1 := syscall.SyscallN(procQueryFullProcessImageNameW.Addr(), uintptr(proc), uintptr(flags), uintptr(unsafe.Pointer(exeName)), uintptr(unsafe.Pointer(size))) if r1 == 0 { err = errnoErr(e1) } @@ -3004,7 +3059,7 @@ func QueryFullProcessImageName(proc Handle, flags uint32, exeName *uint16, size } func QueryInformationJobObject(job Handle, JobObjectInformationClass int32, JobObjectInformation uintptr, JobObjectInformationLength uint32, retlen *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryInformationJobObject.Addr(), 5, uintptr(job), uintptr(JobObjectInformationClass), uintptr(JobObjectInformation), uintptr(JobObjectInformationLength), uintptr(unsafe.Pointer(retlen)), 0) + r1, _, e1 := syscall.SyscallN(procQueryInformationJobObject.Addr(), uintptr(job), uintptr(JobObjectInformationClass), uintptr(JobObjectInformation), uintptr(JobObjectInformationLength), uintptr(unsafe.Pointer(retlen))) if r1 == 0 { err = errnoErr(e1) } @@ -3012,7 +3067,7 @@ func QueryInformationJobObject(job Handle, JobObjectInformationClass int32, JobO } func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) { - r1, _, e1 := syscall.Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0) + r1, _, e1 := syscall.SyscallN(procReadConsoleW.Addr(), uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl))) if r1 == 0 { err = errnoErr(e1) } @@ -3024,7 +3079,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree if watchSubTree { _p0 = 1 } - r1, _, e1 := syscall.Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0) + r1, _, e1 := syscall.SyscallN(procReadDirectoryChangesW.Addr(), uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) if r1 == 0 { err = errnoErr(e1) } @@ -3036,7 +3091,7 @@ func readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) ( if len(buf) > 0 { _p0 = &buf[0] } - r1, _, e1 := syscall.Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) + r1, _, e1 := syscall.SyscallN(procReadFile.Addr(), uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -3044,7 +3099,7 @@ func readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) ( } func ReadProcessMemory(process Handle, baseAddress uintptr, buffer *byte, size uintptr, numberOfBytesRead *uintptr) (err error) { - r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(process), uintptr(baseAddress), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(numberOfBytesRead)), 0) + r1, _, e1 := syscall.SyscallN(procReadProcessMemory.Addr(), uintptr(process), uintptr(baseAddress), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(numberOfBytesRead))) if r1 == 0 { err = errnoErr(e1) } @@ -3052,7 +3107,7 @@ func ReadProcessMemory(process Handle, baseAddress uintptr, buffer *byte, size u } func ReleaseMutex(mutex Handle) (err error) { - r1, _, e1 := syscall.Syscall(procReleaseMutex.Addr(), 1, uintptr(mutex), 0, 0) + r1, _, e1 := syscall.SyscallN(procReleaseMutex.Addr(), uintptr(mutex)) if r1 == 0 { err = errnoErr(e1) } @@ -3060,7 +3115,7 @@ func ReleaseMutex(mutex Handle) (err error) { } func RemoveDirectory(path *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + r1, _, e1 := syscall.SyscallN(procRemoveDirectoryW.Addr(), uintptr(unsafe.Pointer(path))) if r1 == 0 { err = errnoErr(e1) } @@ -3068,7 +3123,7 @@ func RemoveDirectory(path *uint16) (err error) { } func RemoveDllDirectory(cookie uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0) + r1, _, e1 := syscall.SyscallN(procRemoveDllDirectory.Addr(), uintptr(cookie)) if r1 == 0 { err = errnoErr(e1) } @@ -3076,7 +3131,7 @@ func RemoveDllDirectory(cookie uintptr) (err error) { } func ResetEvent(event Handle) (err error) { - r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0) + r1, _, e1 := syscall.SyscallN(procResetEvent.Addr(), uintptr(event)) if r1 == 0 { err = errnoErr(e1) } @@ -3084,7 +3139,7 @@ func ResetEvent(event Handle) (err error) { } func resizePseudoConsole(pconsole Handle, size uint32) (hr error) { - r0, _, _ := syscall.Syscall(procResizePseudoConsole.Addr(), 2, uintptr(pconsole), uintptr(size), 0) + r0, _, _ := syscall.SyscallN(procResizePseudoConsole.Addr(), uintptr(pconsole), uintptr(size)) if r0 != 0 { hr = syscall.Errno(r0) } @@ -3092,7 +3147,7 @@ func resizePseudoConsole(pconsole Handle, size uint32) (hr error) { } func ResumeThread(thread Handle) (ret uint32, err error) { - r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(thread), 0, 0) + r0, _, e1 := syscall.SyscallN(procResumeThread.Addr(), uintptr(thread)) ret = uint32(r0) if ret == 0xffffffff { err = errnoErr(e1) @@ -3101,7 +3156,7 @@ func ResumeThread(thread Handle) (ret uint32, err error) { } func SetCommBreak(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetCommBreak.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -3109,7 +3164,7 @@ func SetCommBreak(handle Handle) (err error) { } func SetCommMask(handle Handle, dwEvtMask uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(dwEvtMask), 0) + r1, _, e1 := syscall.SyscallN(procSetCommMask.Addr(), uintptr(handle), uintptr(dwEvtMask)) if r1 == 0 { err = errnoErr(e1) } @@ -3117,7 +3172,7 @@ func SetCommMask(handle Handle, dwEvtMask uint32) (err error) { } func SetCommState(handle Handle, lpDCB *DCB) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0) + r1, _, e1 := syscall.SyscallN(procSetCommState.Addr(), uintptr(handle), uintptr(unsafe.Pointer(lpDCB))) if r1 == 0 { err = errnoErr(e1) } @@ -3125,7 +3180,7 @@ func SetCommState(handle Handle, lpDCB *DCB) (err error) { } func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) + r1, _, e1 := syscall.SyscallN(procSetCommTimeouts.Addr(), uintptr(handle), uintptr(unsafe.Pointer(timeouts))) if r1 == 0 { err = errnoErr(e1) } @@ -3133,7 +3188,7 @@ func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { } func SetConsoleCP(cp uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetConsoleCP.Addr(), 1, uintptr(cp), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetConsoleCP.Addr(), uintptr(cp)) if r1 == 0 { err = errnoErr(e1) } @@ -3141,7 +3196,7 @@ func SetConsoleCP(cp uint32) (err error) { } func setConsoleCursorPosition(console Handle, position uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetConsoleCursorPosition.Addr(), 2, uintptr(console), uintptr(position), 0) + r1, _, e1 := syscall.SyscallN(procSetConsoleCursorPosition.Addr(), uintptr(console), uintptr(position)) if r1 == 0 { err = errnoErr(e1) } @@ -3149,7 +3204,7 @@ func setConsoleCursorPosition(console Handle, position uint32) (err error) { } func SetConsoleMode(console Handle, mode uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(console), uintptr(mode), 0) + r1, _, e1 := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(console), uintptr(mode)) if r1 == 0 { err = errnoErr(e1) } @@ -3157,7 +3212,7 @@ func SetConsoleMode(console Handle, mode uint32) (err error) { } func SetConsoleOutputCP(cp uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetConsoleOutputCP.Addr(), 1, uintptr(cp), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetConsoleOutputCP.Addr(), uintptr(cp)) if r1 == 0 { err = errnoErr(e1) } @@ -3165,7 +3220,7 @@ func SetConsoleOutputCP(cp uint32) (err error) { } func SetCurrentDirectory(path *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetCurrentDirectoryW.Addr(), uintptr(unsafe.Pointer(path))) if r1 == 0 { err = errnoErr(e1) } @@ -3173,7 +3228,7 @@ func SetCurrentDirectory(path *uint16) (err error) { } func SetDefaultDllDirectories(directoryFlags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetDefaultDllDirectories.Addr(), 1, uintptr(directoryFlags), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetDefaultDllDirectories.Addr(), uintptr(directoryFlags)) if r1 == 0 { err = errnoErr(e1) } @@ -3190,7 +3245,7 @@ func SetDllDirectory(path string) (err error) { } func _SetDllDirectory(path *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procSetDllDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetDllDirectoryW.Addr(), uintptr(unsafe.Pointer(path))) if r1 == 0 { err = errnoErr(e1) } @@ -3198,7 +3253,7 @@ func _SetDllDirectory(path *uint16) (err error) { } func SetEndOfFile(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetEndOfFile.Addr(), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -3206,7 +3261,7 @@ func SetEndOfFile(handle Handle) (err error) { } func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) + r1, _, e1 := syscall.SyscallN(procSetEnvironmentVariableW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value))) if r1 == 0 { err = errnoErr(e1) } @@ -3214,13 +3269,13 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { } func SetErrorMode(mode uint32) (ret uint32) { - r0, _, _ := syscall.Syscall(procSetErrorMode.Addr(), 1, uintptr(mode), 0, 0) + r0, _, _ := syscall.SyscallN(procSetErrorMode.Addr(), uintptr(mode)) ret = uint32(r0) return } func SetEvent(event Handle) (err error) { - r1, _, e1 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(event), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetEvent.Addr(), uintptr(event)) if r1 == 0 { err = errnoErr(e1) } @@ -3228,7 +3283,7 @@ func SetEvent(event Handle) (err error) { } func SetFileAttributes(name *uint16, attrs uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0) + r1, _, e1 := syscall.SyscallN(procSetFileAttributesW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(attrs)) if r1 == 0 { err = errnoErr(e1) } @@ -3236,7 +3291,7 @@ func SetFileAttributes(name *uint16, attrs uint32) (err error) { } func SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error) { - r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(handle), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(handle), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -3244,7 +3299,7 @@ func SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error) } func SetFileInformationByHandle(handle Handle, class uint32, inBuffer *byte, inBufferLen uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetFileInformationByHandle.Addr(), uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen)) if r1 == 0 { err = errnoErr(e1) } @@ -3252,7 +3307,7 @@ func SetFileInformationByHandle(handle Handle, class uint32, inBuffer *byte, inB } func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) { - r0, _, e1 := syscall.Syscall6(procSetFilePointer.Addr(), 4, uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence), 0, 0) + r0, _, e1 := syscall.SyscallN(procSetFilePointer.Addr(), uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence)) newlowoffset = uint32(r0) if newlowoffset == 0xffffffff { err = errnoErr(e1) @@ -3261,7 +3316,7 @@ func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence } func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { - r1, _, e1 := syscall.Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetFileTime.Addr(), uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime))) if r1 == 0 { err = errnoErr(e1) } @@ -3269,7 +3324,7 @@ func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim } func SetFileValidData(handle Handle, validDataLength int64) (err error) { - r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0) + r1, _, e1 := syscall.SyscallN(procSetFileValidData.Addr(), uintptr(handle), uintptr(validDataLength)) if r1 == 0 { err = errnoErr(e1) } @@ -3277,7 +3332,7 @@ func SetFileValidData(handle Handle, validDataLength int64) (err error) { } func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags)) + r1, _, e1 := syscall.SyscallN(procSetHandleInformation.Addr(), uintptr(handle), uintptr(mask), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -3285,7 +3340,7 @@ func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) } func SetInformationJobObject(job Handle, JobObjectInformationClass uint32, JobObjectInformation uintptr, JobObjectInformationLength uint32) (ret int, err error) { - r0, _, e1 := syscall.Syscall6(procSetInformationJobObject.Addr(), 4, uintptr(job), uintptr(JobObjectInformationClass), uintptr(JobObjectInformation), uintptr(JobObjectInformationLength), 0, 0) + r0, _, e1 := syscall.SyscallN(procSetInformationJobObject.Addr(), uintptr(job), uintptr(JobObjectInformationClass), uintptr(JobObjectInformation), uintptr(JobObjectInformationLength)) ret = int(r0) if ret == 0 { err = errnoErr(e1) @@ -3294,7 +3349,7 @@ func SetInformationJobObject(job Handle, JobObjectInformationClass uint32, JobOb } func SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetNamedPipeHandleState.Addr(), 4, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetNamedPipeHandleState.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout))) if r1 == 0 { err = errnoErr(e1) } @@ -3302,7 +3357,7 @@ func SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uin } func SetPriorityClass(process Handle, priorityClass uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetPriorityClass.Addr(), 2, uintptr(process), uintptr(priorityClass), 0) + r1, _, e1 := syscall.SyscallN(procSetPriorityClass.Addr(), uintptr(process), uintptr(priorityClass)) if r1 == 0 { err = errnoErr(e1) } @@ -3314,7 +3369,7 @@ func SetProcessPriorityBoost(process Handle, disable bool) (err error) { if disable { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procSetProcessPriorityBoost.Addr(), 2, uintptr(process), uintptr(_p0), 0) + r1, _, e1 := syscall.SyscallN(procSetProcessPriorityBoost.Addr(), uintptr(process), uintptr(_p0)) if r1 == 0 { err = errnoErr(e1) } @@ -3322,7 +3377,7 @@ func SetProcessPriorityBoost(process Handle, disable bool) (err error) { } func SetProcessShutdownParameters(level uint32, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetProcessShutdownParameters.Addr(), 2, uintptr(level), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procSetProcessShutdownParameters.Addr(), uintptr(level), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -3330,7 +3385,7 @@ func SetProcessShutdownParameters(level uint32, flags uint32) (err error) { } func SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetProcessWorkingSetSizeEx.Addr(), 4, uintptr(hProcess), uintptr(dwMinimumWorkingSetSize), uintptr(dwMaximumWorkingSetSize), uintptr(flags), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetProcessWorkingSetSizeEx.Addr(), uintptr(hProcess), uintptr(dwMinimumWorkingSetSize), uintptr(dwMaximumWorkingSetSize), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -3338,7 +3393,7 @@ func SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr } func SetStdHandle(stdhandle uint32, handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0) + r1, _, e1 := syscall.SyscallN(procSetStdHandle.Addr(), uintptr(stdhandle), uintptr(handle)) if r1 == 0 { err = errnoErr(e1) } @@ -3346,7 +3401,7 @@ func SetStdHandle(stdhandle uint32, handle Handle) (err error) { } func SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procSetVolumeLabelW.Addr(), 2, uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeName)), 0) + r1, _, e1 := syscall.SyscallN(procSetVolumeLabelW.Addr(), uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeName))) if r1 == 0 { err = errnoErr(e1) } @@ -3354,7 +3409,7 @@ func SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) { } func SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procSetVolumeMountPointW.Addr(), 2, uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(unsafe.Pointer(volumeName)), 0) + r1, _, e1 := syscall.SyscallN(procSetVolumeMountPointW.Addr(), uintptr(unsafe.Pointer(volumeMountPoint)), uintptr(unsafe.Pointer(volumeName))) if r1 == 0 { err = errnoErr(e1) } @@ -3362,7 +3417,7 @@ func SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err erro } func SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) { - r1, _, e1 := syscall.Syscall(procSetupComm.Addr(), 3, uintptr(handle), uintptr(dwInQueue), uintptr(dwOutQueue)) + r1, _, e1 := syscall.SyscallN(procSetupComm.Addr(), uintptr(handle), uintptr(dwInQueue), uintptr(dwOutQueue)) if r1 == 0 { err = errnoErr(e1) } @@ -3370,7 +3425,7 @@ func SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) { } func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) { - r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0) + r0, _, e1 := syscall.SyscallN(procSizeofResource.Addr(), uintptr(module), uintptr(resInfo)) size = uint32(r0) if size == 0 { err = errnoErr(e1) @@ -3383,13 +3438,13 @@ func SleepEx(milliseconds uint32, alertable bool) (ret uint32) { if alertable { _p0 = 1 } - r0, _, _ := syscall.Syscall(procSleepEx.Addr(), 2, uintptr(milliseconds), uintptr(_p0), 0) + r0, _, _ := syscall.SyscallN(procSleepEx.Addr(), uintptr(milliseconds), uintptr(_p0)) ret = uint32(r0) return } func TerminateJobObject(job Handle, exitCode uint32) (err error) { - r1, _, e1 := syscall.Syscall(procTerminateJobObject.Addr(), 2, uintptr(job), uintptr(exitCode), 0) + r1, _, e1 := syscall.SyscallN(procTerminateJobObject.Addr(), uintptr(job), uintptr(exitCode)) if r1 == 0 { err = errnoErr(e1) } @@ -3397,7 +3452,7 @@ func TerminateJobObject(job Handle, exitCode uint32) (err error) { } func TerminateProcess(handle Handle, exitcode uint32) (err error) { - r1, _, e1 := syscall.Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0) + r1, _, e1 := syscall.SyscallN(procTerminateProcess.Addr(), uintptr(handle), uintptr(exitcode)) if r1 == 0 { err = errnoErr(e1) } @@ -3405,7 +3460,7 @@ func TerminateProcess(handle Handle, exitcode uint32) (err error) { } func Thread32First(snapshot Handle, threadEntry *ThreadEntry32) (err error) { - r1, _, e1 := syscall.Syscall(procThread32First.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(threadEntry)), 0) + r1, _, e1 := syscall.SyscallN(procThread32First.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(threadEntry))) if r1 == 0 { err = errnoErr(e1) } @@ -3413,7 +3468,7 @@ func Thread32First(snapshot Handle, threadEntry *ThreadEntry32) (err error) { } func Thread32Next(snapshot Handle, threadEntry *ThreadEntry32) (err error) { - r1, _, e1 := syscall.Syscall(procThread32Next.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(threadEntry)), 0) + r1, _, e1 := syscall.SyscallN(procThread32Next.Addr(), uintptr(snapshot), uintptr(unsafe.Pointer(threadEntry))) if r1 == 0 { err = errnoErr(e1) } @@ -3421,7 +3476,7 @@ func Thread32Next(snapshot Handle, threadEntry *ThreadEntry32) (err error) { } func UnlockFileEx(file Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall6(procUnlockFileEx.Addr(), 5, uintptr(file), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped)), 0) + r1, _, e1 := syscall.SyscallN(procUnlockFileEx.Addr(), uintptr(file), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -3429,7 +3484,7 @@ func UnlockFileEx(file Handle, reserved uint32, bytesLow uint32, bytesHigh uint3 } func UnmapViewOfFile(addr uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0) + r1, _, e1 := syscall.SyscallN(procUnmapViewOfFile.Addr(), uintptr(addr)) if r1 == 0 { err = errnoErr(e1) } @@ -3437,7 +3492,7 @@ func UnmapViewOfFile(addr uintptr) (err error) { } func updateProcThreadAttribute(attrlist *ProcThreadAttributeList, flags uint32, attr uintptr, value unsafe.Pointer, size uintptr, prevvalue unsafe.Pointer, returnedsize *uintptr) (err error) { - r1, _, e1 := syscall.Syscall9(procUpdateProcThreadAttribute.Addr(), 7, uintptr(unsafe.Pointer(attrlist)), uintptr(flags), uintptr(attr), uintptr(value), uintptr(size), uintptr(prevvalue), uintptr(unsafe.Pointer(returnedsize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procUpdateProcThreadAttribute.Addr(), uintptr(unsafe.Pointer(attrlist)), uintptr(flags), uintptr(attr), uintptr(value), uintptr(size), uintptr(prevvalue), uintptr(unsafe.Pointer(returnedsize))) if r1 == 0 { err = errnoErr(e1) } @@ -3445,7 +3500,7 @@ func updateProcThreadAttribute(attrlist *ProcThreadAttributeList, flags uint32, } func VirtualAlloc(address uintptr, size uintptr, alloctype uint32, protect uint32) (value uintptr, err error) { - r0, _, e1 := syscall.Syscall6(procVirtualAlloc.Addr(), 4, uintptr(address), uintptr(size), uintptr(alloctype), uintptr(protect), 0, 0) + r0, _, e1 := syscall.SyscallN(procVirtualAlloc.Addr(), uintptr(address), uintptr(size), uintptr(alloctype), uintptr(protect)) value = uintptr(r0) if value == 0 { err = errnoErr(e1) @@ -3454,7 +3509,7 @@ func VirtualAlloc(address uintptr, size uintptr, alloctype uint32, protect uint3 } func VirtualFree(address uintptr, size uintptr, freetype uint32) (err error) { - r1, _, e1 := syscall.Syscall(procVirtualFree.Addr(), 3, uintptr(address), uintptr(size), uintptr(freetype)) + r1, _, e1 := syscall.SyscallN(procVirtualFree.Addr(), uintptr(address), uintptr(size), uintptr(freetype)) if r1 == 0 { err = errnoErr(e1) } @@ -3462,7 +3517,7 @@ func VirtualFree(address uintptr, size uintptr, freetype uint32) (err error) { } func VirtualLock(addr uintptr, length uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0) + r1, _, e1 := syscall.SyscallN(procVirtualLock.Addr(), uintptr(addr), uintptr(length)) if r1 == 0 { err = errnoErr(e1) } @@ -3470,7 +3525,7 @@ func VirtualLock(addr uintptr, length uintptr) (err error) { } func VirtualProtect(address uintptr, size uintptr, newprotect uint32, oldprotect *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procVirtualProtect.Addr(), 4, uintptr(address), uintptr(size), uintptr(newprotect), uintptr(unsafe.Pointer(oldprotect)), 0, 0) + r1, _, e1 := syscall.SyscallN(procVirtualProtect.Addr(), uintptr(address), uintptr(size), uintptr(newprotect), uintptr(unsafe.Pointer(oldprotect))) if r1 == 0 { err = errnoErr(e1) } @@ -3478,7 +3533,7 @@ func VirtualProtect(address uintptr, size uintptr, newprotect uint32, oldprotect } func VirtualProtectEx(process Handle, address uintptr, size uintptr, newProtect uint32, oldProtect *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procVirtualProtectEx.Addr(), 5, uintptr(process), uintptr(address), uintptr(size), uintptr(newProtect), uintptr(unsafe.Pointer(oldProtect)), 0) + r1, _, e1 := syscall.SyscallN(procVirtualProtectEx.Addr(), uintptr(process), uintptr(address), uintptr(size), uintptr(newProtect), uintptr(unsafe.Pointer(oldProtect))) if r1 == 0 { err = errnoErr(e1) } @@ -3486,7 +3541,7 @@ func VirtualProtectEx(process Handle, address uintptr, size uintptr, newProtect } func VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procVirtualQuery.Addr(), 3, uintptr(address), uintptr(unsafe.Pointer(buffer)), uintptr(length)) + r1, _, e1 := syscall.SyscallN(procVirtualQuery.Addr(), uintptr(address), uintptr(unsafe.Pointer(buffer)), uintptr(length)) if r1 == 0 { err = errnoErr(e1) } @@ -3494,7 +3549,7 @@ func VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintpt } func VirtualQueryEx(process Handle, address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) { - r1, _, e1 := syscall.Syscall6(procVirtualQueryEx.Addr(), 4, uintptr(process), uintptr(address), uintptr(unsafe.Pointer(buffer)), uintptr(length), 0, 0) + r1, _, e1 := syscall.SyscallN(procVirtualQueryEx.Addr(), uintptr(process), uintptr(address), uintptr(unsafe.Pointer(buffer)), uintptr(length)) if r1 == 0 { err = errnoErr(e1) } @@ -3502,7 +3557,7 @@ func VirtualQueryEx(process Handle, address uintptr, buffer *MemoryBasicInformat } func VirtualUnlock(addr uintptr, length uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0) + r1, _, e1 := syscall.SyscallN(procVirtualUnlock.Addr(), uintptr(addr), uintptr(length)) if r1 == 0 { err = errnoErr(e1) } @@ -3510,13 +3565,13 @@ func VirtualUnlock(addr uintptr, length uintptr) (err error) { } func WTSGetActiveConsoleSessionId() (sessionID uint32) { - r0, _, _ := syscall.Syscall(procWTSGetActiveConsoleSessionId.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procWTSGetActiveConsoleSessionId.Addr()) sessionID = uint32(r0) return } func WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procWaitCommEvent.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpEvtMask)), uintptr(unsafe.Pointer(lpOverlapped))) + r1, _, e1 := syscall.SyscallN(procWaitCommEvent.Addr(), uintptr(handle), uintptr(unsafe.Pointer(lpEvtMask)), uintptr(unsafe.Pointer(lpOverlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -3528,7 +3583,7 @@ func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMil if waitAll { _p0 = 1 } - r0, _, e1 := syscall.Syscall6(procWaitForMultipleObjects.Addr(), 4, uintptr(count), uintptr(handles), uintptr(_p0), uintptr(waitMilliseconds), 0, 0) + r0, _, e1 := syscall.SyscallN(procWaitForMultipleObjects.Addr(), uintptr(count), uintptr(handles), uintptr(_p0), uintptr(waitMilliseconds)) event = uint32(r0) if event == 0xffffffff { err = errnoErr(e1) @@ -3537,7 +3592,7 @@ func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMil } func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) { - r0, _, e1 := syscall.Syscall(procWaitForSingleObject.Addr(), 2, uintptr(handle), uintptr(waitMilliseconds), 0) + r0, _, e1 := syscall.SyscallN(procWaitForSingleObject.Addr(), uintptr(handle), uintptr(waitMilliseconds)) event = uint32(r0) if event == 0xffffffff { err = errnoErr(e1) @@ -3546,7 +3601,7 @@ func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, } func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) { - r1, _, e1 := syscall.Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0) + r1, _, e1 := syscall.SyscallN(procWriteConsoleW.Addr(), uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved))) if r1 == 0 { err = errnoErr(e1) } @@ -3558,7 +3613,7 @@ func writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) if len(buf) > 0 { _p0 = &buf[0] } - r1, _, e1 := syscall.Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) + r1, _, e1 := syscall.SyscallN(procWriteFile.Addr(), uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -3566,7 +3621,7 @@ func writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) } func WriteProcessMemory(process Handle, baseAddress uintptr, buffer *byte, size uintptr, numberOfBytesWritten *uintptr) (err error) { - r1, _, e1 := syscall.Syscall6(procWriteProcessMemory.Addr(), 5, uintptr(process), uintptr(baseAddress), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(numberOfBytesWritten)), 0) + r1, _, e1 := syscall.SyscallN(procWriteProcessMemory.Addr(), uintptr(process), uintptr(baseAddress), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(numberOfBytesWritten))) if r1 == 0 { err = errnoErr(e1) } @@ -3574,7 +3629,7 @@ func WriteProcessMemory(process Handle, baseAddress uintptr, buffer *byte, size } func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) { - r1, _, e1 := syscall.Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) + r1, _, e1 := syscall.SyscallN(procAcceptEx.Addr(), uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { err = errnoErr(e1) } @@ -3582,12 +3637,12 @@ func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32 } func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) { - syscall.Syscall9(procGetAcceptExSockaddrs.Addr(), 8, uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(lrsa)), uintptr(unsafe.Pointer(lrsalen)), uintptr(unsafe.Pointer(rrsa)), uintptr(unsafe.Pointer(rrsalen)), 0) + syscall.SyscallN(procGetAcceptExSockaddrs.Addr(), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(lrsa)), uintptr(unsafe.Pointer(lrsalen)), uintptr(unsafe.Pointer(rrsa)), uintptr(unsafe.Pointer(rrsalen))) return } func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0) + r1, _, e1 := syscall.SyscallN(procTransmitFile.Addr(), uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -3595,7 +3650,7 @@ func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint } func NetApiBufferFree(buf *byte) (neterr error) { - r0, _, _ := syscall.Syscall(procNetApiBufferFree.Addr(), 1, uintptr(unsafe.Pointer(buf)), 0, 0) + r0, _, _ := syscall.SyscallN(procNetApiBufferFree.Addr(), uintptr(unsafe.Pointer(buf))) if r0 != 0 { neterr = syscall.Errno(r0) } @@ -3603,7 +3658,7 @@ func NetApiBufferFree(buf *byte) (neterr error) { } func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) { - r0, _, _ := syscall.Syscall(procNetGetJoinInformation.Addr(), 3, uintptr(unsafe.Pointer(server)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bufType))) + r0, _, _ := syscall.SyscallN(procNetGetJoinInformation.Addr(), uintptr(unsafe.Pointer(server)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bufType))) if r0 != 0 { neterr = syscall.Errno(r0) } @@ -3611,7 +3666,7 @@ func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (nete } func NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) { - r0, _, _ := syscall.Syscall9(procNetUserEnum.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), uintptr(unsafe.Pointer(resumeHandle)), 0) + r0, _, _ := syscall.SyscallN(procNetUserEnum.Addr(), uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), uintptr(unsafe.Pointer(resumeHandle))) if r0 != 0 { neterr = syscall.Errno(r0) } @@ -3619,7 +3674,7 @@ func NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, pr } func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) { - r0, _, _ := syscall.Syscall6(procNetUserGetInfo.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), 0, 0) + r0, _, _ := syscall.SyscallN(procNetUserGetInfo.Addr(), uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf))) if r0 != 0 { neterr = syscall.Errno(r0) } @@ -3627,7 +3682,7 @@ func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **by } func NtCreateFile(handle *Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, allocationSize *int64, attributes uint32, share uint32, disposition uint32, options uint32, eabuffer uintptr, ealength uint32) (ntstatus error) { - r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(attributes), uintptr(share), uintptr(disposition), uintptr(options), uintptr(eabuffer), uintptr(ealength), 0) + r0, _, _ := syscall.SyscallN(procNtCreateFile.Addr(), uintptr(unsafe.Pointer(handle)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(attributes), uintptr(share), uintptr(disposition), uintptr(options), uintptr(eabuffer), uintptr(ealength)) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3635,7 +3690,7 @@ func NtCreateFile(handle *Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO } func NtCreateNamedPipeFile(pipe *Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (ntstatus error) { - r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) + r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3643,7 +3698,7 @@ func NtCreateNamedPipeFile(pipe *Handle, access uint32, oa *OBJECT_ATTRIBUTES, i } func NtQueryInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32, retLen *uint32) (ntstatus error) { - r0, _, _ := syscall.Syscall6(procNtQueryInformationProcess.Addr(), 5, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), uintptr(unsafe.Pointer(retLen)), 0) + r0, _, _ := syscall.SyscallN(procNtQueryInformationProcess.Addr(), uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), uintptr(unsafe.Pointer(retLen))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3651,7 +3706,7 @@ func NtQueryInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe } func NtQuerySystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoLen uint32, retLen *uint32) (ntstatus error) { - r0, _, _ := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(sysInfoClass), uintptr(sysInfo), uintptr(sysInfoLen), uintptr(unsafe.Pointer(retLen)), 0, 0) + r0, _, _ := syscall.SyscallN(procNtQuerySystemInformation.Addr(), uintptr(sysInfoClass), uintptr(sysInfo), uintptr(sysInfoLen), uintptr(unsafe.Pointer(retLen))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3659,7 +3714,7 @@ func NtQuerySystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInf } func NtSetInformationFile(handle Handle, iosb *IO_STATUS_BLOCK, inBuffer *byte, inBufferLen uint32, class uint32) (ntstatus error) { - r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), uintptr(class), 0) + r0, _, _ := syscall.SyscallN(procNtSetInformationFile.Addr(), uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), uintptr(class)) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3667,7 +3722,7 @@ func NtSetInformationFile(handle Handle, iosb *IO_STATUS_BLOCK, inBuffer *byte, } func NtSetInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32) (ntstatus error) { - r0, _, _ := syscall.Syscall6(procNtSetInformationProcess.Addr(), 4, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), 0, 0) + r0, _, _ := syscall.SyscallN(procNtSetInformationProcess.Addr(), uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen)) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3675,7 +3730,7 @@ func NtSetInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.P } func NtSetSystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoLen uint32) (ntstatus error) { - r0, _, _ := syscall.Syscall(procNtSetSystemInformation.Addr(), 3, uintptr(sysInfoClass), uintptr(sysInfo), uintptr(sysInfoLen)) + r0, _, _ := syscall.SyscallN(procNtSetSystemInformation.Addr(), uintptr(sysInfoClass), uintptr(sysInfo), uintptr(sysInfoLen)) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3683,13 +3738,13 @@ func NtSetSystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoL } func RtlAddFunctionTable(functionTable *RUNTIME_FUNCTION, entryCount uint32, baseAddress uintptr) (ret bool) { - r0, _, _ := syscall.Syscall(procRtlAddFunctionTable.Addr(), 3, uintptr(unsafe.Pointer(functionTable)), uintptr(entryCount), uintptr(baseAddress)) + r0, _, _ := syscall.SyscallN(procRtlAddFunctionTable.Addr(), uintptr(unsafe.Pointer(functionTable)), uintptr(entryCount), uintptr(baseAddress)) ret = r0 != 0 return } func RtlDefaultNpAcl(acl **ACL) (ntstatus error) { - r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(acl)), 0, 0) + r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(acl))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3697,13 +3752,13 @@ func RtlDefaultNpAcl(acl **ACL) (ntstatus error) { } func RtlDeleteFunctionTable(functionTable *RUNTIME_FUNCTION) (ret bool) { - r0, _, _ := syscall.Syscall(procRtlDeleteFunctionTable.Addr(), 1, uintptr(unsafe.Pointer(functionTable)), 0, 0) + r0, _, _ := syscall.SyscallN(procRtlDeleteFunctionTable.Addr(), uintptr(unsafe.Pointer(functionTable))) ret = r0 != 0 return } func RtlDosPathNameToNtPathName(dosName *uint16, ntName *NTUnicodeString, ntFileNamePart *uint16, relativeName *RTL_RELATIVE_NAME) (ntstatus error) { - r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U_WithStatus.Addr(), 4, uintptr(unsafe.Pointer(dosName)), uintptr(unsafe.Pointer(ntName)), uintptr(unsafe.Pointer(ntFileNamePart)), uintptr(unsafe.Pointer(relativeName)), 0, 0) + r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U_WithStatus.Addr(), uintptr(unsafe.Pointer(dosName)), uintptr(unsafe.Pointer(ntName)), uintptr(unsafe.Pointer(ntFileNamePart)), uintptr(unsafe.Pointer(relativeName))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3711,7 +3766,7 @@ func RtlDosPathNameToNtPathName(dosName *uint16, ntName *NTUnicodeString, ntFile } func RtlDosPathNameToRelativeNtPathName(dosName *uint16, ntName *NTUnicodeString, ntFileNamePart *uint16, relativeName *RTL_RELATIVE_NAME) (ntstatus error) { - r0, _, _ := syscall.Syscall6(procRtlDosPathNameToRelativeNtPathName_U_WithStatus.Addr(), 4, uintptr(unsafe.Pointer(dosName)), uintptr(unsafe.Pointer(ntName)), uintptr(unsafe.Pointer(ntFileNamePart)), uintptr(unsafe.Pointer(relativeName)), 0, 0) + r0, _, _ := syscall.SyscallN(procRtlDosPathNameToRelativeNtPathName_U_WithStatus.Addr(), uintptr(unsafe.Pointer(dosName)), uintptr(unsafe.Pointer(ntName)), uintptr(unsafe.Pointer(ntFileNamePart)), uintptr(unsafe.Pointer(relativeName))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3719,18 +3774,18 @@ func RtlDosPathNameToRelativeNtPathName(dosName *uint16, ntName *NTUnicodeString } func RtlGetCurrentPeb() (peb *PEB) { - r0, _, _ := syscall.Syscall(procRtlGetCurrentPeb.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procRtlGetCurrentPeb.Addr()) peb = (*PEB)(unsafe.Pointer(r0)) return } func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { - syscall.Syscall(procRtlGetNtVersionNumbers.Addr(), 3, uintptr(unsafe.Pointer(majorVersion)), uintptr(unsafe.Pointer(minorVersion)), uintptr(unsafe.Pointer(buildNumber))) + syscall.SyscallN(procRtlGetNtVersionNumbers.Addr(), uintptr(unsafe.Pointer(majorVersion)), uintptr(unsafe.Pointer(minorVersion)), uintptr(unsafe.Pointer(buildNumber))) return } func rtlGetVersion(info *OsVersionInfoEx) (ntstatus error) { - r0, _, _ := syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0) + r0, _, _ := syscall.SyscallN(procRtlGetVersion.Addr(), uintptr(unsafe.Pointer(info))) if r0 != 0 { ntstatus = NTStatus(r0) } @@ -3738,23 +3793,23 @@ func rtlGetVersion(info *OsVersionInfoEx) (ntstatus error) { } func RtlInitString(destinationString *NTString, sourceString *byte) { - syscall.Syscall(procRtlInitString.Addr(), 2, uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString)), 0) + syscall.SyscallN(procRtlInitString.Addr(), uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString))) return } func RtlInitUnicodeString(destinationString *NTUnicodeString, sourceString *uint16) { - syscall.Syscall(procRtlInitUnicodeString.Addr(), 2, uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString)), 0) + syscall.SyscallN(procRtlInitUnicodeString.Addr(), uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString))) return } func rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) { - r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(ntstatus), 0, 0) + r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(ntstatus)) ret = syscall.Errno(r0) return } func clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) { - r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0) + r0, _, _ := syscall.SyscallN(procCLSIDFromString.Addr(), uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -3762,7 +3817,7 @@ func clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) { } func coCreateGuid(pguid *GUID) (ret error) { - r0, _, _ := syscall.Syscall(procCoCreateGuid.Addr(), 1, uintptr(unsafe.Pointer(pguid)), 0, 0) + r0, _, _ := syscall.SyscallN(procCoCreateGuid.Addr(), uintptr(unsafe.Pointer(pguid))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -3770,7 +3825,7 @@ func coCreateGuid(pguid *GUID) (ret error) { } func CoGetObject(name *uint16, bindOpts *BIND_OPTS3, guid *GUID, functionTable **uintptr) (ret error) { - r0, _, _ := syscall.Syscall6(procCoGetObject.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bindOpts)), uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(functionTable)), 0, 0) + r0, _, _ := syscall.SyscallN(procCoGetObject.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bindOpts)), uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(functionTable))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -3778,7 +3833,7 @@ func CoGetObject(name *uint16, bindOpts *BIND_OPTS3, guid *GUID, functionTable * } func CoInitializeEx(reserved uintptr, coInit uint32) (ret error) { - r0, _, _ := syscall.Syscall(procCoInitializeEx.Addr(), 2, uintptr(reserved), uintptr(coInit), 0) + r0, _, _ := syscall.SyscallN(procCoInitializeEx.Addr(), uintptr(reserved), uintptr(coInit)) if r0 != 0 { ret = syscall.Errno(r0) } @@ -3786,23 +3841,23 @@ func CoInitializeEx(reserved uintptr, coInit uint32) (ret error) { } func CoTaskMemFree(address unsafe.Pointer) { - syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(address), 0, 0) + syscall.SyscallN(procCoTaskMemFree.Addr(), uintptr(address)) return } func CoUninitialize() { - syscall.Syscall(procCoUninitialize.Addr(), 0, 0, 0, 0) + syscall.SyscallN(procCoUninitialize.Addr()) return } func stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) { - r0, _, _ := syscall.Syscall(procStringFromGUID2.Addr(), 3, uintptr(unsafe.Pointer(rguid)), uintptr(unsafe.Pointer(lpsz)), uintptr(cchMax)) + r0, _, _ := syscall.SyscallN(procStringFromGUID2.Addr(), uintptr(unsafe.Pointer(rguid)), uintptr(unsafe.Pointer(lpsz)), uintptr(cchMax)) chars = int32(r0) return } func EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procEnumProcessModules.Addr(), 4, uintptr(process), uintptr(unsafe.Pointer(module)), uintptr(cb), uintptr(unsafe.Pointer(cbNeeded)), 0, 0) + r1, _, e1 := syscall.SyscallN(procEnumProcessModules.Addr(), uintptr(process), uintptr(unsafe.Pointer(module)), uintptr(cb), uintptr(unsafe.Pointer(cbNeeded))) if r1 == 0 { err = errnoErr(e1) } @@ -3810,7 +3865,7 @@ func EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uin } func EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procEnumProcessModulesEx.Addr(), 5, uintptr(process), uintptr(unsafe.Pointer(module)), uintptr(cb), uintptr(unsafe.Pointer(cbNeeded)), uintptr(filterFlag), 0) + r1, _, e1 := syscall.SyscallN(procEnumProcessModulesEx.Addr(), uintptr(process), uintptr(unsafe.Pointer(module)), uintptr(cb), uintptr(unsafe.Pointer(cbNeeded)), uintptr(filterFlag)) if r1 == 0 { err = errnoErr(e1) } @@ -3818,7 +3873,7 @@ func EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *u } func enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(processIds)), uintptr(nSize), uintptr(unsafe.Pointer(bytesReturned))) + r1, _, e1 := syscall.SyscallN(procEnumProcesses.Addr(), uintptr(unsafe.Pointer(processIds)), uintptr(nSize), uintptr(unsafe.Pointer(bytesReturned))) if r1 == 0 { err = errnoErr(e1) } @@ -3826,7 +3881,7 @@ func enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err } func GetModuleBaseName(process Handle, module Handle, baseName *uint16, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetModuleBaseNameW.Addr(), 4, uintptr(process), uintptr(module), uintptr(unsafe.Pointer(baseName)), uintptr(size), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetModuleBaseNameW.Addr(), uintptr(process), uintptr(module), uintptr(unsafe.Pointer(baseName)), uintptr(size)) if r1 == 0 { err = errnoErr(e1) } @@ -3834,7 +3889,7 @@ func GetModuleBaseName(process Handle, module Handle, baseName *uint16, size uin } func GetModuleFileNameEx(process Handle, module Handle, filename *uint16, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetModuleFileNameExW.Addr(), 4, uintptr(process), uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetModuleFileNameExW.Addr(), uintptr(process), uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size)) if r1 == 0 { err = errnoErr(e1) } @@ -3842,7 +3897,7 @@ func GetModuleFileNameEx(process Handle, module Handle, filename *uint16, size u } func GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetModuleInformation.Addr(), 4, uintptr(process), uintptr(module), uintptr(unsafe.Pointer(modinfo)), uintptr(cb), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetModuleInformation.Addr(), uintptr(process), uintptr(module), uintptr(unsafe.Pointer(modinfo)), uintptr(cb)) if r1 == 0 { err = errnoErr(e1) } @@ -3850,7 +3905,7 @@ func GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb } func QueryWorkingSetEx(process Handle, pv uintptr, cb uint32) (err error) { - r1, _, e1 := syscall.Syscall(procQueryWorkingSetEx.Addr(), 3, uintptr(process), uintptr(pv), uintptr(cb)) + r1, _, e1 := syscall.SyscallN(procQueryWorkingSetEx.Addr(), uintptr(process), uintptr(pv), uintptr(cb)) if r1 == 0 { err = errnoErr(e1) } @@ -3862,7 +3917,7 @@ func SubscribeServiceChangeNotifications(service Handle, eventType uint32, callb if ret != nil { return } - r0, _, _ := syscall.Syscall6(procSubscribeServiceChangeNotifications.Addr(), 5, uintptr(service), uintptr(eventType), uintptr(callback), uintptr(callbackCtx), uintptr(unsafe.Pointer(subscription)), 0) + r0, _, _ := syscall.SyscallN(procSubscribeServiceChangeNotifications.Addr(), uintptr(service), uintptr(eventType), uintptr(callback), uintptr(callbackCtx), uintptr(unsafe.Pointer(subscription))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -3874,12 +3929,12 @@ func UnsubscribeServiceChangeNotifications(subscription uintptr) (err error) { if err != nil { return } - syscall.Syscall(procUnsubscribeServiceChangeNotifications.Addr(), 1, uintptr(subscription), 0, 0) + syscall.SyscallN(procUnsubscribeServiceChangeNotifications.Addr(), uintptr(subscription)) return } func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize))) + r1, _, e1 := syscall.SyscallN(procGetUserNameExW.Addr(), uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize))) if r1&0xff == 0 { err = errnoErr(e1) } @@ -3887,7 +3942,7 @@ func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err er } func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0) + r1, _, e1 := syscall.SyscallN(procTranslateNameW.Addr(), uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize))) if r1&0xff == 0 { err = errnoErr(e1) } @@ -3895,7 +3950,7 @@ func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint } func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiBuildDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType)) + r1, _, e1 := syscall.SyscallN(procSetupDiBuildDriverInfoList.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType)) if r1 == 0 { err = errnoErr(e1) } @@ -3903,7 +3958,7 @@ func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoDa } func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiCallClassInstaller.Addr(), 3, uintptr(installFunction), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData))) + r1, _, e1 := syscall.SyscallN(procSetupDiCallClassInstaller.Addr(), uintptr(installFunction), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -3911,7 +3966,7 @@ func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInf } func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiCancelDriverInfoSearch.Addr(), 1, uintptr(deviceInfoSet), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetupDiCancelDriverInfoSearch.Addr(), uintptr(deviceInfoSet)) if r1 == 0 { err = errnoErr(e1) } @@ -3919,7 +3974,7 @@ func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) { } func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameExW.Addr(), 6, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved)) + r1, _, e1 := syscall.SyscallN(procSetupDiClassGuidsFromNameExW.Addr(), uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved)) if r1 == 0 { err = errnoErr(e1) } @@ -3927,7 +3982,7 @@ func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *GUID, classGu } func setupDiClassNameFromGuidEx(classGUID *GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiClassNameFromGuidExW.Addr(), 6, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved)) + r1, _, e1 := syscall.SyscallN(procSetupDiClassNameFromGuidExW.Addr(), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved)) if r1 == 0 { err = errnoErr(e1) } @@ -3935,7 +3990,7 @@ func setupDiClassNameFromGuidEx(classGUID *GUID, className *uint16, classNameSiz } func setupDiCreateDeviceInfoListEx(classGUID *GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) { - r0, _, e1 := syscall.Syscall6(procSetupDiCreateDeviceInfoListExW.Addr(), 4, uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0) + r0, _, e1 := syscall.SyscallN(procSetupDiCreateDeviceInfoListExW.Addr(), uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved)) handle = DevInfo(r0) if handle == DevInfo(InvalidHandle) { err = errnoErr(e1) @@ -3944,7 +3999,7 @@ func setupDiCreateDeviceInfoListEx(classGUID *GUID, hwndParent uintptr, machineN } func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) { - r1, _, e1 := syscall.Syscall9(procSetupDiCreateDeviceInfoW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData)), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetupDiCreateDeviceInfoW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -3952,7 +4007,7 @@ func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUI } func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(deviceInfoSet), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetupDiDestroyDeviceInfoList.Addr(), uintptr(deviceInfoSet)) if r1 == 0 { err = errnoErr(e1) } @@ -3960,7 +4015,7 @@ func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) { } func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiDestroyDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType)) + r1, _, e1 := syscall.SyscallN(procSetupDiDestroyDriverInfoList.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType)) if r1 == 0 { err = errnoErr(e1) } @@ -3968,7 +4023,7 @@ func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfo } func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData))) + r1, _, e1 := syscall.SyscallN(procSetupDiEnumDeviceInfo.Addr(), uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -3976,7 +4031,7 @@ func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfo } func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiEnumDriverInfoW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData)), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiEnumDriverInfoW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -3984,7 +4039,7 @@ func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, d } func setupDiGetClassDevsEx(classGUID *GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) { - r0, _, e1 := syscall.Syscall9(procSetupDiGetClassDevsExW.Addr(), 7, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0) + r0, _, e1 := syscall.SyscallN(procSetupDiGetClassDevsExW.Addr(), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved)) handle = DevInfo(r0) if handle == DevInfo(InvalidHandle) { err = errnoErr(e1) @@ -3993,7 +4048,7 @@ func setupDiGetClassDevsEx(classGUID *GUID, Enumerator *uint16, hwndParent uintp } func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiGetClassInstallParamsW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize)), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiGetClassInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize))) if r1 == 0 { err = errnoErr(e1) } @@ -4001,7 +4056,7 @@ func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfo } func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInfoListDetailW.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData)), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceInfoListDetailW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData))) if r1 == 0 { err = errnoErr(e1) } @@ -4009,7 +4064,7 @@ func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailDa } func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams))) + r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams))) if r1 == 0 { err = errnoErr(e1) } @@ -4017,7 +4072,7 @@ func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInf } func setupDiGetDeviceInstanceId(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, instanceId *uint16, instanceIdSize uint32, instanceIdRequiredSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(instanceId)), uintptr(instanceIdSize), uintptr(unsafe.Pointer(instanceIdRequiredSize)), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceInstanceIdW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(instanceId)), uintptr(instanceIdSize), uintptr(unsafe.Pointer(instanceIdRequiredSize))) if r1 == 0 { err = errnoErr(e1) } @@ -4025,7 +4080,7 @@ func setupDiGetDeviceInstanceId(deviceInfoSet DevInfo, deviceInfoData *DevInfoDa } func setupDiGetDeviceProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, propertyKey *DEVPROPKEY, propertyType *DEVPROPTYPE, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procSetupDiGetDevicePropertyW.Addr(), 8, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(propertyKey)), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiGetDevicePropertyW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(propertyKey)), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(flags)) if r1 == 0 { err = errnoErr(e1) } @@ -4033,7 +4088,7 @@ func setupDiGetDeviceProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData } func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceRegistryPropertyW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize))) if r1 == 0 { err = errnoErr(e1) } @@ -4041,7 +4096,7 @@ func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *Dev } func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiGetDriverInfoDetailW.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize))) + r1, _, e1 := syscall.SyscallN(procSetupDiGetDriverInfoDetailW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize))) if r1 == 0 { err = errnoErr(e1) } @@ -4049,7 +4104,7 @@ func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoDa } func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiGetSelectedDevice.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -4057,7 +4112,7 @@ func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData } func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData))) + r1, _, e1 := syscall.SyscallN(procSetupDiGetSelectedDriverW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -4065,7 +4120,7 @@ func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData } func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key Handle, err error) { - r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired)) + r0, _, e1 := syscall.SyscallN(procSetupDiOpenDevRegKey.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired)) key = Handle(r0) if key == InvalidHandle { err = errnoErr(e1) @@ -4074,7 +4129,7 @@ func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Sc } func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiSetClassInstallParamsW.Addr(), 4, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), 0, 0) + r1, _, e1 := syscall.SyscallN(procSetupDiSetClassInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize)) if r1 == 0 { err = errnoErr(e1) } @@ -4082,7 +4137,7 @@ func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfo } func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiSetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams))) + r1, _, e1 := syscall.SyscallN(procSetupDiSetDeviceInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams))) if r1 == 0 { err = errnoErr(e1) } @@ -4090,7 +4145,7 @@ func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInf } func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiSetDeviceRegistryPropertyW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiSetDeviceRegistryPropertyW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize)) if r1 == 0 { err = errnoErr(e1) } @@ -4098,7 +4153,7 @@ func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *Dev } func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0) + r1, _, e1 := syscall.SyscallN(procSetupDiSetSelectedDevice.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -4106,7 +4161,7 @@ func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData } func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData))) + r1, _, e1 := syscall.SyscallN(procSetupDiSetSelectedDriverW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData))) if r1 == 0 { err = errnoErr(e1) } @@ -4114,7 +4169,7 @@ func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData } func setupUninstallOEMInf(infFileName *uint16, flags SUOI, reserved uintptr) (err error) { - r1, _, e1 := syscall.Syscall(procSetupUninstallOEMInfW.Addr(), 3, uintptr(unsafe.Pointer(infFileName)), uintptr(flags), uintptr(reserved)) + r1, _, e1 := syscall.SyscallN(procSetupUninstallOEMInfW.Addr(), uintptr(unsafe.Pointer(infFileName)), uintptr(flags), uintptr(reserved)) if r1 == 0 { err = errnoErr(e1) } @@ -4122,7 +4177,7 @@ func setupUninstallOEMInf(infFileName *uint16, flags SUOI, reserved uintptr) (er } func commandLineToArgv(cmd *uint16, argc *int32) (argv **uint16, err error) { - r0, _, e1 := syscall.Syscall(procCommandLineToArgvW.Addr(), 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0) + r0, _, e1 := syscall.SyscallN(procCommandLineToArgvW.Addr(), uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc))) argv = (**uint16)(unsafe.Pointer(r0)) if argv == nil { err = errnoErr(e1) @@ -4131,7 +4186,7 @@ func commandLineToArgv(cmd *uint16, argc *int32) (argv **uint16, err error) { } func shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **uint16) (ret error) { - r0, _, _ := syscall.Syscall6(procSHGetKnownFolderPath.Addr(), 4, uintptr(unsafe.Pointer(id)), uintptr(flags), uintptr(token), uintptr(unsafe.Pointer(path)), 0, 0) + r0, _, _ := syscall.SyscallN(procSHGetKnownFolderPath.Addr(), uintptr(unsafe.Pointer(id)), uintptr(flags), uintptr(token), uintptr(unsafe.Pointer(path))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -4139,7 +4194,7 @@ func shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **u } func ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) { - r1, _, e1 := syscall.Syscall6(procShellExecuteW.Addr(), 6, uintptr(hwnd), uintptr(unsafe.Pointer(verb)), uintptr(unsafe.Pointer(file)), uintptr(unsafe.Pointer(args)), uintptr(unsafe.Pointer(cwd)), uintptr(showCmd)) + r1, _, e1 := syscall.SyscallN(procShellExecuteW.Addr(), uintptr(hwnd), uintptr(unsafe.Pointer(verb)), uintptr(unsafe.Pointer(file)), uintptr(unsafe.Pointer(args)), uintptr(unsafe.Pointer(cwd)), uintptr(showCmd)) if r1 <= 32 { err = errnoErr(e1) } @@ -4147,12 +4202,12 @@ func ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *ui } func EnumChildWindows(hwnd HWND, enumFunc uintptr, param unsafe.Pointer) { - syscall.Syscall(procEnumChildWindows.Addr(), 3, uintptr(hwnd), uintptr(enumFunc), uintptr(param)) + syscall.SyscallN(procEnumChildWindows.Addr(), uintptr(hwnd), uintptr(enumFunc), uintptr(param)) return } func EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) { - r1, _, e1 := syscall.Syscall(procEnumWindows.Addr(), 2, uintptr(enumFunc), uintptr(param), 0) + r1, _, e1 := syscall.SyscallN(procEnumWindows.Addr(), uintptr(enumFunc), uintptr(param)) if r1 == 0 { err = errnoErr(e1) } @@ -4160,7 +4215,7 @@ func EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) { } func ExitWindowsEx(flags uint32, reason uint32) (err error) { - r1, _, e1 := syscall.Syscall(procExitWindowsEx.Addr(), 2, uintptr(flags), uintptr(reason), 0) + r1, _, e1 := syscall.SyscallN(procExitWindowsEx.Addr(), uintptr(flags), uintptr(reason)) if r1 == 0 { err = errnoErr(e1) } @@ -4168,7 +4223,7 @@ func ExitWindowsEx(flags uint32, reason uint32) (err error) { } func GetClassName(hwnd HWND, className *uint16, maxCount int32) (copied int32, err error) { - r0, _, e1 := syscall.Syscall(procGetClassNameW.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(className)), uintptr(maxCount)) + r0, _, e1 := syscall.SyscallN(procGetClassNameW.Addr(), uintptr(hwnd), uintptr(unsafe.Pointer(className)), uintptr(maxCount)) copied = int32(r0) if copied == 0 { err = errnoErr(e1) @@ -4177,19 +4232,19 @@ func GetClassName(hwnd HWND, className *uint16, maxCount int32) (copied int32, e } func GetDesktopWindow() (hwnd HWND) { - r0, _, _ := syscall.Syscall(procGetDesktopWindow.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetDesktopWindow.Addr()) hwnd = HWND(r0) return } func GetForegroundWindow() (hwnd HWND) { - r0, _, _ := syscall.Syscall(procGetForegroundWindow.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetForegroundWindow.Addr()) hwnd = HWND(r0) return } func GetGUIThreadInfo(thread uint32, info *GUIThreadInfo) (err error) { - r1, _, e1 := syscall.Syscall(procGetGUIThreadInfo.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(info)), 0) + r1, _, e1 := syscall.SyscallN(procGetGUIThreadInfo.Addr(), uintptr(thread), uintptr(unsafe.Pointer(info))) if r1 == 0 { err = errnoErr(e1) } @@ -4197,19 +4252,19 @@ func GetGUIThreadInfo(thread uint32, info *GUIThreadInfo) (err error) { } func GetKeyboardLayout(tid uint32) (hkl Handle) { - r0, _, _ := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(tid), 0, 0) + r0, _, _ := syscall.SyscallN(procGetKeyboardLayout.Addr(), uintptr(tid)) hkl = Handle(r0) return } func GetShellWindow() (shellWindow HWND) { - r0, _, _ := syscall.Syscall(procGetShellWindow.Addr(), 0, 0, 0, 0) + r0, _, _ := syscall.SyscallN(procGetShellWindow.Addr()) shellWindow = HWND(r0) return } func GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetWindowThreadProcessId.Addr(), 2, uintptr(hwnd), uintptr(unsafe.Pointer(pid)), 0) + r0, _, e1 := syscall.SyscallN(procGetWindowThreadProcessId.Addr(), uintptr(hwnd), uintptr(unsafe.Pointer(pid))) tid = uint32(r0) if tid == 0 { err = errnoErr(e1) @@ -4218,25 +4273,25 @@ func GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) { } func IsWindow(hwnd HWND) (isWindow bool) { - r0, _, _ := syscall.Syscall(procIsWindow.Addr(), 1, uintptr(hwnd), 0, 0) + r0, _, _ := syscall.SyscallN(procIsWindow.Addr(), uintptr(hwnd)) isWindow = r0 != 0 return } func IsWindowUnicode(hwnd HWND) (isUnicode bool) { - r0, _, _ := syscall.Syscall(procIsWindowUnicode.Addr(), 1, uintptr(hwnd), 0, 0) + r0, _, _ := syscall.SyscallN(procIsWindowUnicode.Addr(), uintptr(hwnd)) isUnicode = r0 != 0 return } func IsWindowVisible(hwnd HWND) (isVisible bool) { - r0, _, _ := syscall.Syscall(procIsWindowVisible.Addr(), 1, uintptr(hwnd), 0, 0) + r0, _, _ := syscall.SyscallN(procIsWindowVisible.Addr(), uintptr(hwnd)) isVisible = r0 != 0 return } func LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) { - r0, _, e1 := syscall.Syscall(procLoadKeyboardLayoutW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(flags), 0) + r0, _, e1 := syscall.SyscallN(procLoadKeyboardLayoutW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags)) hkl = Handle(r0) if hkl == 0 { err = errnoErr(e1) @@ -4245,7 +4300,7 @@ func LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) { } func MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) { - r0, _, e1 := syscall.Syscall6(procMessageBoxW.Addr(), 4, uintptr(hwnd), uintptr(unsafe.Pointer(text)), uintptr(unsafe.Pointer(caption)), uintptr(boxtype), 0, 0) + r0, _, e1 := syscall.SyscallN(procMessageBoxW.Addr(), uintptr(hwnd), uintptr(unsafe.Pointer(text)), uintptr(unsafe.Pointer(caption)), uintptr(boxtype)) ret = int32(r0) if ret == 0 { err = errnoErr(e1) @@ -4254,13 +4309,13 @@ func MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret i } func ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32) { - r0, _, _ := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(vkey), uintptr(scancode), uintptr(unsafe.Pointer(keystate)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(flags), uintptr(hkl), 0, 0) + r0, _, _ := syscall.SyscallN(procToUnicodeEx.Addr(), uintptr(vkey), uintptr(scancode), uintptr(unsafe.Pointer(keystate)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(flags), uintptr(hkl)) ret = int32(r0) return } func UnloadKeyboardLayout(hkl Handle) (err error) { - r1, _, e1 := syscall.Syscall(procUnloadKeyboardLayout.Addr(), 1, uintptr(hkl), 0, 0) + r1, _, e1 := syscall.SyscallN(procUnloadKeyboardLayout.Addr(), uintptr(hkl)) if r1 == 0 { err = errnoErr(e1) } @@ -4272,7 +4327,7 @@ func CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) ( if inheritExisting { _p0 = 1 } - r1, _, e1 := syscall.Syscall(procCreateEnvironmentBlock.Addr(), 3, uintptr(unsafe.Pointer(block)), uintptr(token), uintptr(_p0)) + r1, _, e1 := syscall.SyscallN(procCreateEnvironmentBlock.Addr(), uintptr(unsafe.Pointer(block)), uintptr(token), uintptr(_p0)) if r1 == 0 { err = errnoErr(e1) } @@ -4280,7 +4335,7 @@ func CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) ( } func DestroyEnvironmentBlock(block *uint16) (err error) { - r1, _, e1 := syscall.Syscall(procDestroyEnvironmentBlock.Addr(), 1, uintptr(unsafe.Pointer(block)), 0, 0) + r1, _, e1 := syscall.SyscallN(procDestroyEnvironmentBlock.Addr(), uintptr(unsafe.Pointer(block))) if r1 == 0 { err = errnoErr(e1) } @@ -4288,7 +4343,7 @@ func DestroyEnvironmentBlock(block *uint16) (err error) { } func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { - r1, _, e1 := syscall.Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen))) + r1, _, e1 := syscall.SyscallN(procGetUserProfileDirectoryW.Addr(), uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen))) if r1 == 0 { err = errnoErr(e1) } @@ -4305,7 +4360,7 @@ func GetFileVersionInfoSize(filename string, zeroHandle *Handle) (bufSize uint32 } func _GetFileVersionInfoSize(filename *uint16, zeroHandle *Handle) (bufSize uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetFileVersionInfoSizeW.Addr(), 2, uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(zeroHandle)), 0) + r0, _, e1 := syscall.SyscallN(procGetFileVersionInfoSizeW.Addr(), uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(zeroHandle))) bufSize = uint32(r0) if bufSize == 0 { err = errnoErr(e1) @@ -4323,7 +4378,7 @@ func GetFileVersionInfo(filename string, handle uint32, bufSize uint32, buffer u } func _GetFileVersionInfo(filename *uint16, handle uint32, bufSize uint32, buffer unsafe.Pointer) (err error) { - r1, _, e1 := syscall.Syscall6(procGetFileVersionInfoW.Addr(), 4, uintptr(unsafe.Pointer(filename)), uintptr(handle), uintptr(bufSize), uintptr(buffer), 0, 0) + r1, _, e1 := syscall.SyscallN(procGetFileVersionInfoW.Addr(), uintptr(unsafe.Pointer(filename)), uintptr(handle), uintptr(bufSize), uintptr(buffer)) if r1 == 0 { err = errnoErr(e1) } @@ -4340,7 +4395,7 @@ func VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer } func _VerQueryValue(block unsafe.Pointer, subBlock *uint16, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procVerQueryValueW.Addr(), 4, uintptr(block), uintptr(unsafe.Pointer(subBlock)), uintptr(pointerToBufferPointer), uintptr(unsafe.Pointer(bufSize)), 0, 0) + r1, _, e1 := syscall.SyscallN(procVerQueryValueW.Addr(), uintptr(block), uintptr(unsafe.Pointer(subBlock)), uintptr(pointerToBufferPointer), uintptr(unsafe.Pointer(bufSize))) if r1 == 0 { err = errnoErr(e1) } @@ -4348,7 +4403,7 @@ func _VerQueryValue(block unsafe.Pointer, subBlock *uint16, pointerToBufferPoint } func TimeBeginPeriod(period uint32) (err error) { - r1, _, e1 := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0) + r1, _, e1 := syscall.SyscallN(proctimeBeginPeriod.Addr(), uintptr(period)) if r1 != 0 { err = errnoErr(e1) } @@ -4356,7 +4411,7 @@ func TimeBeginPeriod(period uint32) (err error) { } func TimeEndPeriod(period uint32) (err error) { - r1, _, e1 := syscall.Syscall(proctimeEndPeriod.Addr(), 1, uintptr(period), 0, 0) + r1, _, e1 := syscall.SyscallN(proctimeEndPeriod.Addr(), uintptr(period)) if r1 != 0 { err = errnoErr(e1) } @@ -4364,7 +4419,7 @@ func TimeEndPeriod(period uint32) (err error) { } func WinVerifyTrustEx(hwnd HWND, actionId *GUID, data *WinTrustData) (ret error) { - r0, _, _ := syscall.Syscall(procWinVerifyTrustEx.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(actionId)), uintptr(unsafe.Pointer(data))) + r0, _, _ := syscall.SyscallN(procWinVerifyTrustEx.Addr(), uintptr(hwnd), uintptr(unsafe.Pointer(actionId)), uintptr(unsafe.Pointer(data))) if r0 != 0 { ret = syscall.Errno(r0) } @@ -4372,12 +4427,12 @@ func WinVerifyTrustEx(hwnd HWND, actionId *GUID, data *WinTrustData) (ret error) } func FreeAddrInfoW(addrinfo *AddrinfoW) { - syscall.Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0) + syscall.SyscallN(procFreeAddrInfoW.Addr(), uintptr(unsafe.Pointer(addrinfo))) return } func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) { - r0, _, _ := syscall.Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0) + r0, _, _ := syscall.SyscallN(procGetAddrInfoW.Addr(), uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result))) if r0 != 0 { sockerr = syscall.Errno(r0) } @@ -4385,7 +4440,7 @@ func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, resul } func WSACleanup() (err error) { - r1, _, e1 := syscall.Syscall(procWSACleanup.Addr(), 0, 0, 0, 0) + r1, _, e1 := syscall.SyscallN(procWSACleanup.Addr()) if r1 == socket_error { err = errnoErr(e1) } @@ -4393,7 +4448,7 @@ func WSACleanup() (err error) { } func WSADuplicateSocket(s Handle, processID uint32, info *WSAProtocolInfo) (err error) { - r1, _, e1 := syscall.Syscall(procWSADuplicateSocketW.Addr(), 3, uintptr(s), uintptr(processID), uintptr(unsafe.Pointer(info))) + r1, _, e1 := syscall.SyscallN(procWSADuplicateSocketW.Addr(), uintptr(s), uintptr(processID), uintptr(unsafe.Pointer(info))) if r1 != 0 { err = errnoErr(e1) } @@ -4401,7 +4456,7 @@ func WSADuplicateSocket(s Handle, processID uint32, info *WSAProtocolInfo) (err } func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) { - r0, _, e1 := syscall.Syscall(procWSAEnumProtocolsW.Addr(), 3, uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength))) + r0, _, e1 := syscall.SyscallN(procWSAEnumProtocolsW.Addr(), uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength))) n = int32(r0) if n == -1 { err = errnoErr(e1) @@ -4414,7 +4469,7 @@ func WSAGetOverlappedResult(h Handle, o *Overlapped, bytes *uint32, wait bool, f if wait { _p0 = 1 } - r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0) + r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags))) if r1 == 0 { err = errnoErr(e1) } @@ -4422,7 +4477,7 @@ func WSAGetOverlappedResult(h Handle, o *Overlapped, bytes *uint32, wait bool, f } func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) { - r1, _, e1 := syscall.Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) + r1, _, e1 := syscall.SyscallN(procWSAIoctl.Addr(), uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) if r1 == socket_error { err = errnoErr(e1) } @@ -4430,7 +4485,7 @@ func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbo } func WSALookupServiceBegin(querySet *WSAQUERYSET, flags uint32, handle *Handle) (err error) { - r1, _, e1 := syscall.Syscall(procWSALookupServiceBeginW.Addr(), 3, uintptr(unsafe.Pointer(querySet)), uintptr(flags), uintptr(unsafe.Pointer(handle))) + r1, _, e1 := syscall.SyscallN(procWSALookupServiceBeginW.Addr(), uintptr(unsafe.Pointer(querySet)), uintptr(flags), uintptr(unsafe.Pointer(handle))) if r1 == socket_error { err = errnoErr(e1) } @@ -4438,7 +4493,7 @@ func WSALookupServiceBegin(querySet *WSAQUERYSET, flags uint32, handle *Handle) } func WSALookupServiceEnd(handle Handle) (err error) { - r1, _, e1 := syscall.Syscall(procWSALookupServiceEnd.Addr(), 1, uintptr(handle), 0, 0) + r1, _, e1 := syscall.SyscallN(procWSALookupServiceEnd.Addr(), uintptr(handle)) if r1 == socket_error { err = errnoErr(e1) } @@ -4446,7 +4501,7 @@ func WSALookupServiceEnd(handle Handle) (err error) { } func WSALookupServiceNext(handle Handle, flags uint32, size *int32, querySet *WSAQUERYSET) (err error) { - r1, _, e1 := syscall.Syscall6(procWSALookupServiceNextW.Addr(), 4, uintptr(handle), uintptr(flags), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(querySet)), 0, 0) + r1, _, e1 := syscall.SyscallN(procWSALookupServiceNextW.Addr(), uintptr(handle), uintptr(flags), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(querySet))) if r1 == socket_error { err = errnoErr(e1) } @@ -4454,7 +4509,7 @@ func WSALookupServiceNext(handle Handle, flags uint32, size *int32, querySet *WS } func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) { - r1, _, e1 := syscall.Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) + r1, _, e1 := syscall.SyscallN(procWSARecv.Addr(), uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if r1 == socket_error { err = errnoErr(e1) } @@ -4462,7 +4517,7 @@ func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32 } func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) { - r1, _, e1 := syscall.Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + r1, _, e1 := syscall.SyscallN(procWSARecvFrom.Addr(), uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if r1 == socket_error { err = errnoErr(e1) } @@ -4470,7 +4525,7 @@ func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui } func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) { - r1, _, e1 := syscall.Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) + r1, _, e1 := syscall.SyscallN(procWSASend.Addr(), uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if r1 == socket_error { err = errnoErr(e1) } @@ -4478,7 +4533,7 @@ func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, } func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) { - r1, _, e1 := syscall.Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + r1, _, e1 := syscall.SyscallN(procWSASendTo.Addr(), uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if r1 == socket_error { err = errnoErr(e1) } @@ -4486,7 +4541,7 @@ func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 } func WSASocket(af int32, typ int32, protocol int32, protoInfo *WSAProtocolInfo, group uint32, flags uint32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall6(procWSASocketW.Addr(), 6, uintptr(af), uintptr(typ), uintptr(protocol), uintptr(unsafe.Pointer(protoInfo)), uintptr(group), uintptr(flags)) + r0, _, e1 := syscall.SyscallN(procWSASocketW.Addr(), uintptr(af), uintptr(typ), uintptr(protocol), uintptr(unsafe.Pointer(protoInfo)), uintptr(group), uintptr(flags)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -4495,7 +4550,7 @@ func WSASocket(af int32, typ int32, protocol int32, protoInfo *WSAProtocolInfo, } func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { - r0, _, _ := syscall.Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) + r0, _, _ := syscall.SyscallN(procWSAStartup.Addr(), uintptr(verreq), uintptr(unsafe.Pointer(data))) if r0 != 0 { sockerr = syscall.Errno(r0) } @@ -4503,7 +4558,7 @@ func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { } func bind(s Handle, name unsafe.Pointer, namelen int32) (err error) { - r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen)) if r1 == socket_error { err = errnoErr(e1) } @@ -4511,7 +4566,7 @@ func bind(s Handle, name unsafe.Pointer, namelen int32) (err error) { } func Closesocket(s Handle) (err error) { - r1, _, e1 := syscall.Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0) + r1, _, e1 := syscall.SyscallN(procclosesocket.Addr(), uintptr(s)) if r1 == socket_error { err = errnoErr(e1) } @@ -4519,7 +4574,7 @@ func Closesocket(s Handle) (err error) { } func connect(s Handle, name unsafe.Pointer, namelen int32) (err error) { - r1, _, e1 := syscall.Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + r1, _, e1 := syscall.SyscallN(procconnect.Addr(), uintptr(s), uintptr(name), uintptr(namelen)) if r1 == socket_error { err = errnoErr(e1) } @@ -4536,7 +4591,7 @@ func GetHostByName(name string) (h *Hostent, err error) { } func _GetHostByName(name *byte) (h *Hostent, err error) { - r0, _, e1 := syscall.Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(name)), 0, 0) + r0, _, e1 := syscall.SyscallN(procgethostbyname.Addr(), uintptr(unsafe.Pointer(name))) h = (*Hostent)(unsafe.Pointer(r0)) if h == nil { err = errnoErr(e1) @@ -4545,7 +4600,7 @@ func _GetHostByName(name *byte) (h *Hostent, err error) { } func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { - r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) if r1 == socket_error { err = errnoErr(e1) } @@ -4562,7 +4617,7 @@ func GetProtoByName(name string) (p *Protoent, err error) { } func _GetProtoByName(name *byte) (p *Protoent, err error) { - r0, _, e1 := syscall.Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(name)), 0, 0) + r0, _, e1 := syscall.SyscallN(procgetprotobyname.Addr(), uintptr(unsafe.Pointer(name))) p = (*Protoent)(unsafe.Pointer(r0)) if p == nil { err = errnoErr(e1) @@ -4585,7 +4640,7 @@ func GetServByName(name string, proto string) (s *Servent, err error) { } func _GetServByName(name *byte, proto *byte) (s *Servent, err error) { - r0, _, e1 := syscall.Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(proto)), 0) + r0, _, e1 := syscall.SyscallN(procgetservbyname.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(proto))) s = (*Servent)(unsafe.Pointer(r0)) if s == nil { err = errnoErr(e1) @@ -4594,7 +4649,7 @@ func _GetServByName(name *byte, proto *byte) (s *Servent, err error) { } func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { - r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) if r1 == socket_error { err = errnoErr(e1) } @@ -4602,7 +4657,7 @@ func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { } func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) { - r1, _, e1 := syscall.Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0) + r1, _, e1 := syscall.SyscallN(procgetsockopt.Addr(), uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen))) if r1 == socket_error { err = errnoErr(e1) } @@ -4610,7 +4665,7 @@ func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int3 } func listen(s Handle, backlog int32) (err error) { - r1, _, e1 := syscall.Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0) + r1, _, e1 := syscall.SyscallN(proclisten.Addr(), uintptr(s), uintptr(backlog)) if r1 == socket_error { err = errnoErr(e1) } @@ -4618,7 +4673,7 @@ func listen(s Handle, backlog int32) (err error) { } func Ntohs(netshort uint16) (u uint16) { - r0, _, _ := syscall.Syscall(procntohs.Addr(), 1, uintptr(netshort), 0, 0) + r0, _, _ := syscall.SyscallN(procntohs.Addr(), uintptr(netshort)) u = uint16(r0) return } @@ -4628,7 +4683,7 @@ func recvfrom(s Handle, buf []byte, flags int32, from *RawSockaddrAny, fromlen * if len(buf) > 0 { _p0 = &buf[0] } - r0, _, e1 := syscall.Syscall6(procrecvfrom.Addr(), 6, uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + r0, _, e1 := syscall.SyscallN(procrecvfrom.Addr(), uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int32(r0) if n == -1 { err = errnoErr(e1) @@ -4641,7 +4696,7 @@ func sendto(s Handle, buf []byte, flags int32, to unsafe.Pointer, tolen int32) ( if len(buf) > 0 { _p0 = &buf[0] } - r1, _, e1 := syscall.Syscall6(procsendto.Addr(), 6, uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(tolen)) + r1, _, e1 := syscall.SyscallN(procsendto.Addr(), uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(tolen)) if r1 == socket_error { err = errnoErr(e1) } @@ -4649,7 +4704,7 @@ func sendto(s Handle, buf []byte, flags int32, to unsafe.Pointer, tolen int32) ( } func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) { - r1, _, e1 := syscall.Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0) + r1, _, e1 := syscall.SyscallN(procsetsockopt.Addr(), uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen)) if r1 == socket_error { err = errnoErr(e1) } @@ -4657,7 +4712,7 @@ func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32 } func shutdown(s Handle, how int32) (err error) { - r1, _, e1 := syscall.Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0) + r1, _, e1 := syscall.SyscallN(procshutdown.Addr(), uintptr(s), uintptr(how)) if r1 == socket_error { err = errnoErr(e1) } @@ -4665,7 +4720,7 @@ func shutdown(s Handle, how int32) (err error) { } func socket(af int32, typ int32, protocol int32) (handle Handle, err error) { - r0, _, e1 := syscall.Syscall(procsocket.Addr(), 3, uintptr(af), uintptr(typ), uintptr(protocol)) + r0, _, e1 := syscall.SyscallN(procsocket.Addr(), uintptr(af), uintptr(typ), uintptr(protocol)) handle = Handle(r0) if handle == InvalidHandle { err = errnoErr(e1) @@ -4674,7 +4729,7 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) { } func WTSEnumerateSessions(handle Handle, reserved uint32, version uint32, sessions **WTS_SESSION_INFO, count *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procWTSEnumerateSessionsW.Addr(), 5, uintptr(handle), uintptr(reserved), uintptr(version), uintptr(unsafe.Pointer(sessions)), uintptr(unsafe.Pointer(count)), 0) + r1, _, e1 := syscall.SyscallN(procWTSEnumerateSessionsW.Addr(), uintptr(handle), uintptr(reserved), uintptr(version), uintptr(unsafe.Pointer(sessions)), uintptr(unsafe.Pointer(count))) if r1 == 0 { err = errnoErr(e1) } @@ -4682,12 +4737,12 @@ func WTSEnumerateSessions(handle Handle, reserved uint32, version uint32, sessio } func WTSFreeMemory(ptr uintptr) { - syscall.Syscall(procWTSFreeMemory.Addr(), 1, uintptr(ptr), 0, 0) + syscall.SyscallN(procWTSFreeMemory.Addr(), uintptr(ptr)) return } func WTSQueryUserToken(session uint32, token *Token) (err error) { - r1, _, e1 := syscall.Syscall(procWTSQueryUserToken.Addr(), 2, uintptr(session), uintptr(unsafe.Pointer(token)), 0) + r1, _, e1 := syscall.SyscallN(procWTSQueryUserToken.Addr(), uintptr(session), uintptr(unsafe.Pointer(token))) if r1 == 0 { err = errnoErr(e1) } diff --git a/vendor/golang.org/x/text/unicode/bidi/core.go b/vendor/golang.org/x/text/unicode/bidi/core.go index 9d2ae547..fb827323 100644 --- a/vendor/golang.org/x/text/unicode/bidi/core.go +++ b/vendor/golang.org/x/text/unicode/bidi/core.go @@ -427,13 +427,6 @@ type isolatingRunSequence struct { func (i *isolatingRunSequence) Len() int { return len(i.indexes) } -func maxLevel(a, b level) level { - if a > b { - return a - } - return b -} - // Rule X10, second bullet: Determine the start-of-sequence (sos) and end-of-sequence (eos) types, // either L or R, for each isolating run sequence. func (p *paragraph) isolatingRunSequence(indexes []int) *isolatingRunSequence { @@ -474,8 +467,8 @@ func (p *paragraph) isolatingRunSequence(indexes []int) *isolatingRunSequence { indexes: indexes, types: types, level: level, - sos: typeForLevel(maxLevel(prevLevel, level)), - eos: typeForLevel(maxLevel(succLevel, level)), + sos: typeForLevel(max(prevLevel, level)), + eos: typeForLevel(max(succLevel, level)), } } diff --git a/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go b/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go index bf1aba0e..7b9f01af 100644 --- a/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go +++ b/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go @@ -9,7 +9,7 @@ import "google.golang.org/protobuf/types/descriptorpb" const ( Minimum = descriptorpb.Edition_EDITION_PROTO2 - Maximum = descriptorpb.Edition_EDITION_2023 + Maximum = descriptorpb.Edition_EDITION_2024 // MaximumKnown is the maximum edition that is known to Go Protobuf, but not // declared as supported. In other words: end users cannot use it, but diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go index 688aabe4..dbcf90b8 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go @@ -72,9 +72,10 @@ type ( EditionFeatures EditionFeatures } FileL2 struct { - Options func() protoreflect.ProtoMessage - Imports FileImports - Locations SourceLocations + Options func() protoreflect.ProtoMessage + Imports FileImports + OptionImports func() protoreflect.FileImports + Locations SourceLocations } // EditionFeatures is a frequently-instantiated struct, so please take care @@ -126,12 +127,9 @@ func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd } func (fd *File) Parent() protoreflect.Descriptor { return nil } func (fd *File) Index() int { return 0 } func (fd *File) Syntax() protoreflect.Syntax { return fd.L1.Syntax } - -// Not exported and just used to reconstruct the original FileDescriptor proto -func (fd *File) Edition() int32 { return int32(fd.L1.Edition) } -func (fd *File) Name() protoreflect.Name { return fd.L1.Package.Name() } -func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package } -func (fd *File) IsPlaceholder() bool { return false } +func (fd *File) Name() protoreflect.Name { return fd.L1.Package.Name() } +func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package } +func (fd *File) IsPlaceholder() bool { return false } func (fd *File) Options() protoreflect.ProtoMessage { if f := fd.lazyInit().Options; f != nil { return f() @@ -150,6 +148,16 @@ func (fd *File) Format(s fmt.State, r rune) { descfmt.FormatD func (fd *File) ProtoType(protoreflect.FileDescriptor) {} func (fd *File) ProtoInternal(pragma.DoNotImplement) {} +// The next two are not part of the FileDescriptor interface. They are just used to reconstruct +// the original FileDescriptor proto. +func (fd *File) Edition() int32 { return int32(fd.L1.Edition) } +func (fd *File) OptionImports() protoreflect.FileImports { + if f := fd.lazyInit().OptionImports; f != nil { + return f() + } + return emptyFiles +} + func (fd *File) lazyInit() *FileL2 { if atomic.LoadUint32(&fd.once) == 0 { fd.lazyInitOnce() @@ -182,9 +190,9 @@ type ( L2 *EnumL2 // protected by fileDesc.once } EnumL1 struct { - eagerValues bool // controls whether EnumL2.Values is already populated - EditionFeatures EditionFeatures + Visibility int32 + eagerValues bool // controls whether EnumL2.Values is already populated } EnumL2 struct { Options func() protoreflect.ProtoMessage @@ -219,6 +227,11 @@ func (ed *Enum) ReservedNames() protoreflect.Names { return &ed.lazyInit() func (ed *Enum) ReservedRanges() protoreflect.EnumRanges { return &ed.lazyInit().ReservedRanges } func (ed *Enum) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) } func (ed *Enum) ProtoType(protoreflect.EnumDescriptor) {} + +// This is not part of the EnumDescriptor interface. It is just used to reconstruct +// the original FileDescriptor proto. +func (ed *Enum) Visibility() int32 { return ed.L1.Visibility } + func (ed *Enum) lazyInit() *EnumL2 { ed.L0.ParentFile.lazyInit() // implicitly initializes L2 return ed.L2 @@ -244,13 +257,13 @@ type ( L2 *MessageL2 // protected by fileDesc.once } MessageL1 struct { - Enums Enums - Messages Messages - Extensions Extensions - IsMapEntry bool // promoted from google.protobuf.MessageOptions - IsMessageSet bool // promoted from google.protobuf.MessageOptions - + Enums Enums + Messages Messages + Extensions Extensions EditionFeatures EditionFeatures + Visibility int32 + IsMapEntry bool // promoted from google.protobuf.MessageOptions + IsMessageSet bool // promoted from google.protobuf.MessageOptions } MessageL2 struct { Options func() protoreflect.ProtoMessage @@ -319,6 +332,11 @@ func (md *Message) Messages() protoreflect.MessageDescriptors { return &md.L func (md *Message) Extensions() protoreflect.ExtensionDescriptors { return &md.L1.Extensions } func (md *Message) ProtoType(protoreflect.MessageDescriptor) {} func (md *Message) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) } + +// This is not part of the MessageDescriptor interface. It is just used to reconstruct +// the original FileDescriptor proto. +func (md *Message) Visibility() int32 { return md.L1.Visibility } + func (md *Message) lazyInit() *MessageL2 { md.L0.ParentFile.lazyInit() // implicitly initializes L2 return md.L2 diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go index d2f54949..e91860f5 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go @@ -284,6 +284,13 @@ func (ed *Enum) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protorefl case genid.EnumDescriptorProto_Value_field_number: numValues++ } + case protowire.VarintType: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + switch num { + case genid.EnumDescriptorProto_Visibility_field_number: + ed.L1.Visibility = int32(v) + } default: m := protowire.ConsumeFieldValue(num, typ, b) b = b[m:] @@ -365,6 +372,13 @@ func (md *Message) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protor md.unmarshalSeedOptions(v) } prevField = num + case protowire.VarintType: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + switch num { + case genid.DescriptorProto_Visibility_field_number: + md.L1.Visibility = int32(v) + } default: m := protowire.ConsumeFieldValue(num, typ, b) b = b[m:] diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go index d4c94458..dd31faae 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go @@ -134,6 +134,7 @@ func (fd *File) unmarshalFull(b []byte) { var enumIdx, messageIdx, extensionIdx, serviceIdx int var rawOptions []byte + var optionImports []string fd.L2 = new(FileL2) for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) @@ -157,6 +158,8 @@ func (fd *File) unmarshalFull(b []byte) { imp = PlaceholderFile(path) } fd.L2.Imports = append(fd.L2.Imports, protoreflect.FileImport{FileDescriptor: imp}) + case genid.FileDescriptorProto_OptionDependency_field_number: + optionImports = append(optionImports, sb.MakeString(v)) case genid.FileDescriptorProto_EnumType_field_number: fd.L1.Enums.List[enumIdx].unmarshalFull(v, sb) enumIdx++ @@ -178,6 +181,23 @@ func (fd *File) unmarshalFull(b []byte) { } } fd.L2.Options = fd.builder.optionsUnmarshaler(&descopts.File, rawOptions) + if len(optionImports) > 0 { + var imps FileImports + var once sync.Once + fd.L2.OptionImports = func() protoreflect.FileImports { + once.Do(func() { + imps = make(FileImports, len(optionImports)) + for i, path := range optionImports { + imp, _ := fd.builder.FileRegistry.FindFileByPath(path) + if imp == nil { + imp = PlaceholderFile(path) + } + imps[i] = protoreflect.FileImport{FileDescriptor: imp} + } + }) + return &imps + } + } } func (ed *Enum) unmarshalFull(b []byte, sb *strs.Builder) { diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/editions.go b/vendor/google.golang.org/protobuf/internal/filedesc/editions.go index a0aad277..66ba9068 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/editions.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/editions.go @@ -13,8 +13,10 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" ) -var defaultsCache = make(map[Edition]EditionFeatures) -var defaultsKeys = []Edition{} +var ( + defaultsCache = make(map[Edition]EditionFeatures) + defaultsKeys = []Edition{} +) func init() { unmarshalEditionDefaults(editiondefaults.Defaults) @@ -41,7 +43,7 @@ func unmarshalGoFeature(b []byte, parent EditionFeatures) EditionFeatures { b = b[m:] parent.StripEnumPrefix = int(v) default: - panic(fmt.Sprintf("unkown field number %d while unmarshalling GoFeatures", num)) + panic(fmt.Sprintf("unknown field number %d while unmarshalling GoFeatures", num)) } } return parent @@ -76,7 +78,7 @@ func unmarshalFeatureSet(b []byte, parent EditionFeatures) EditionFeatures { // DefaultSymbolVisibility is enforced in protoc, runtimes should not // inspect this value. default: - panic(fmt.Sprintf("unkown field number %d while unmarshalling FeatureSet", num)) + panic(fmt.Sprintf("unknown field number %d while unmarshalling FeatureSet", num)) } case protowire.BytesType: v, m := protowire.ConsumeBytes(b) @@ -150,7 +152,7 @@ func unmarshalEditionDefaults(b []byte) { _, m := protowire.ConsumeVarint(b) b = b[m:] default: - panic(fmt.Sprintf("unkown field number %d while unmarshalling EditionDefault", num)) + panic(fmt.Sprintf("unknown field number %d while unmarshalling EditionDefault", num)) } } } diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go index 697d1c14..77de0f23 100644 --- a/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/vendor/google.golang.org/protobuf/internal/version/version.go @@ -52,7 +52,7 @@ import ( const ( Major = 1 Minor = 36 - Patch = 8 + Patch = 10 PreRelease = "" ) diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go index 823dbf3b..9196288e 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go @@ -152,6 +152,28 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot imp := &f.L2.Imports[i] imps.importPublic(imp.Imports()) } + if len(fd.GetOptionDependency()) > 0 { + optionImports := make(filedesc.FileImports, len(fd.GetOptionDependency())) + for i, path := range fd.GetOptionDependency() { + imp := &optionImports[i] + f, err := r.FindFileByPath(path) + if err == protoregistry.NotFound { + // We always allow option imports to be unresolvable. + f = filedesc.PlaceholderFile(path) + } else if err != nil { + return nil, errors.New("could not resolve import %q: %v", path, err) + } + imp.FileDescriptor = f + + if imps[imp.Path()] { + return nil, errors.New("already imported %q", path) + } + imps[imp.Path()] = true + } + f.L2.OptionImports = func() protoreflect.FileImports { + return &optionImports + } + } // Handle source locations. f.L2.Locations.File = f diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go index 9da34998..c826ad04 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go @@ -29,6 +29,7 @@ func (r descsByName) initEnumDeclarations(eds []*descriptorpb.EnumDescriptorProt e.L2.Options = func() protoreflect.ProtoMessage { return opts } } e.L1.EditionFeatures = mergeEditionFeatures(parent, ed.GetOptions().GetFeatures()) + e.L1.Visibility = int32(ed.GetVisibility()) for _, s := range ed.GetReservedName() { e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s)) } @@ -70,6 +71,7 @@ func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProt return nil, err } m.L1.EditionFeatures = mergeEditionFeatures(parent, md.GetOptions().GetFeatures()) + m.L1.Visibility = int32(md.GetVisibility()) if opts := md.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.MessageOptions) m.L2.Options = func() protoreflect.ProtoMessage { return opts } diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go b/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go index 9b880aa8..6f91074e 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go @@ -70,16 +70,27 @@ func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileD if syntax := file.Syntax(); syntax != protoreflect.Proto2 && syntax.IsValid() { p.Syntax = proto.String(file.Syntax().String()) } + desc := file + if fileImportDesc, ok := file.(protoreflect.FileImport); ok { + desc = fileImportDesc.FileDescriptor + } if file.Syntax() == protoreflect.Editions { - desc := file - if fileImportDesc, ok := file.(protoreflect.FileImport); ok { - desc = fileImportDesc.FileDescriptor - } - if editionsInterface, ok := desc.(interface{ Edition() int32 }); ok { p.Edition = descriptorpb.Edition(editionsInterface.Edition()).Enum() } } + type hasOptionImports interface { + OptionImports() protoreflect.FileImports + } + if opts, ok := desc.(hasOptionImports); ok { + if optionImports := opts.OptionImports(); optionImports.Len() > 0 { + optionDeps := make([]string, optionImports.Len()) + for i := range optionImports.Len() { + optionDeps[i] = optionImports.Get(i).Path() + } + p.OptionDependency = optionDeps + } + } return p } @@ -123,6 +134,14 @@ func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.Des for i, names := 0, message.ReservedNames(); i < names.Len(); i++ { p.ReservedName = append(p.ReservedName, string(names.Get(i))) } + type hasVisibility interface { + Visibility() int32 + } + if vis, ok := message.(hasVisibility); ok { + if visibility := vis.Visibility(); visibility > 0 { + p.Visibility = descriptorpb.SymbolVisibility(visibility).Enum() + } + } return p } @@ -216,6 +235,14 @@ func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumD for i, names := 0, enum.ReservedNames(); i < names.Len(); i++ { p.ReservedName = append(p.ReservedName, string(names.Get(i))) } + type hasVisibility interface { + Visibility() int32 + } + if vis, ok := enum.(hasVisibility); ok { + if visibility := vis.Visibility(); visibility > 0 { + p.Visibility = descriptorpb.SymbolVisibility(visibility).Enum() + } + } return p } diff --git a/vendor/maunium.net/go/mautrix/.editorconfig b/vendor/maunium.net/go/mautrix/.editorconfig new file mode 100644 index 00000000..1a167e7e --- /dev/null +++ b/vendor/maunium.net/go/mautrix/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{yaml,yml}] +indent_style = space + +[provisioning.yaml] +indent_size = 2 diff --git a/vendor/maunium.net/go/mautrix/.gitignore b/vendor/maunium.net/go/mautrix/.gitignore new file mode 100644 index 00000000..c01f2f30 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/.gitignore @@ -0,0 +1,4 @@ +.idea/ +.vscode/ +*.db* +*.log diff --git a/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml b/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml new file mode 100644 index 00000000..0b9785ae --- /dev/null +++ b/vendor/maunium.net/go/mautrix/.pre-commit-config.yaml @@ -0,0 +1,30 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + exclude_types: [markdown] + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + - repo: https://github.com/tekwizely/pre-commit-golang + rev: v1.0.0-rc.1 + hooks: + - id: go-imports-repo + args: + - "-local" + - "maunium.net/go/mautrix" + - "-w" + - id: go-vet-repo-mod + - id: go-mod-tidy + # TODO enable this + #- id: go-staticcheck-repo-mod + + - repo: https://github.com/beeper/pre-commit-go + rev: v0.4.2 + hooks: + - id: prevent-literal-http-methods + - id: zerolog-ban-global-log + - id: zerolog-ban-msgf + - id: zerolog-use-stringer diff --git a/vendor/maunium.net/go/mautrix/CHANGELOG.md b/vendor/maunium.net/go/mautrix/CHANGELOG.md new file mode 100644 index 00000000..f59e6853 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/CHANGELOG.md @@ -0,0 +1,1262 @@ +## v0.25.2 (2025-10-16) + +* **Breaking change *(id)*** Split `UserID.ParseAndValidate` into + `ParseAndValidateRelaxed` and `ParseAndValidateStrict`. Strict is the old + behavior, but most users likely want the relaxed version, as there are real + users whose user IDs aren't valid under the strict rules. +* *(crypto)* Added helper methods for generating and verifying with recovery + keys. +* *(bridgev2/matrix)* Added config option to automatically generate a recovery + key for the bridge bot and self-sign the bridge's device. +* *(bridgev2/matrix)* Added initial support for using appservice/MSC3202 mode + for encryption with standard servers like Synapse. +* *(bridgev2)* Added optional support for implicit read receipts. +* *(bridgev2)* Added interface for deleting chats on remote network. +* *(bridgev2)* Added local enforcement of media duration and size limits. +* *(bridgev2)* Extended event duration logging to log any event taking too long. +* *(bridgev2)* Improved validation in group creation provisioning API. +* *(event)* Added event type constant for poll end events. +* *(client)* Added wrapper for searching user directory. +* *(client)* Improved support for managing [MSC4140] delayed events. +* *(crypto/helper)* Changed default sync handling to not block on waiting for + decryption keys. On initial sync, keys won't be requested at all by default. +* *(crypto)* Fixed olm unwedging not working (regressed in v0.25.1). +* *(bridgev2)* Fixed various bugs with migrating to split portals. +* *(event)* Fixed poll start events having incorrect null `m.relates_to`. +* *(client)* Fixed `RespUserProfile` losing standard fields when re-marshaling. +* *(federation)* Fixed various bugs in event auth. + +## v0.25.1 (2025-09-16) + +* *(client)* Fixed HTTP method of delete devices API call + (thanks to [@fmseals] in [#393]). +* *(client)* Added wrappers for [MSC4323]: User suspension & locking endpoints + (thanks to [@nexy7574] in [#407]). +* *(client)* Stabilized support for extensible profiles. +* *(client)* Stabilized support for `state_after` in sync. +* *(client)* Removed deprecated MSC2716 requests. +* *(crypto)* Added fallback to ensure `m.relates_to` is always copied even if + the content struct doesn't implement `Relatable`. +* *(crypto)* Changed olm unwedging to ignore newly created sessions if they + haven't been used successfully in either direction. +* *(federation)* Added utilities for generating, parsing, validating and + authorizing PDUs. + * Note: the new PDU code depends on `GOEXPERIMENT=jsonv2` +* *(event)* Added `is_animated` flag from [MSC4230] to file info. +* *(event)* Added types for [MSC4332]: In-room bot commands. +* *(event)* Added missing poll end event type for [MSC3381]. +* *(appservice)* Fixed URLs not being escaped properly when using unix socket + for homeserver connections. +* *(format)* Added more helpers for forming markdown links. +* *(event,bridgev2)* Added support for Beeper's disappearing message state event. +* *(bridgev2)* Redesigned group creation interface and added support in commands + and provisioning API. +* *(bridgev2)* Added GetEvent to Matrix interface to allow network connectors to + get an old event. The method is best effort only, as some configurations don't + allow fetching old events. +* *(bridgev2)* Added shared logic for provisioning that can be reused by the + API, commands and other sources. +* *(bridgev2)* Fixed mentions and URL previews not being copied over when + caption and media are merged. +* *(bridgev2)* Removed config option to change provisioning API prefix, which + had already broken in the previous release. + +[@fmseals]: https://github.com/fmseals +[#393]: https://github.com/mautrix/go/pull/393 +[#407]: https://github.com/mautrix/go/pull/407 +[MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381 +[MSC4230]: https://github.com/matrix-org/matrix-spec-proposals/pull/4230 +[MSC4323]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323 +[MSC4332]: https://github.com/matrix-org/matrix-spec-proposals/pull/4332 + +## v0.25.0 (2025-08-16) + +* Bumped minimum Go version to 1.24. +* **Breaking change *(appservice,bridgev2,federation)*** Replaced gorilla/mux + with standard library ServeMux. +* *(client,bridgev2)* Added support for creator power in room v12. +* *(client)* Added option to not set `User-Agent` header for improved Wasm + compatibility. +* *(bridgev2)* Added support for following tombstones. +* *(bridgev2)* Added interface for getting arbitrary state event from Matrix. +* *(bridgev2)* Added batching to disappearing message queue to ensure it doesn't + use too many resources even if there are a large number of messages. +* *(bridgev2/commands)* Added support for canceling QR login with `cancel` + command. +* *(client)* Added option to override HTTP client used for .well-known + resolution. +* *(crypto/backup)* Added method for encrypting key backup session without + private keys. +* *(event->id)* Moved room version type and constants to id package. +* *(bridgev2)* Bots in DM portals will now be added to the functional members + state event to hide them from the room name calculation. +* *(bridgev2)* Changed message delete handling to ignore "delete for me" events + if there are multiple Matrix users in the room. +* *(format/htmlparser)* Changed text processing to collapse multiple spaces into + one when outside `pre`/`code` tags. +* *(format/htmlparser)* Removed link suffix in plaintext output when link text + is only missing protocol part of href. + * e.g. `example.com` will turn into + `example.com` rather than `example.com (https://example.com)` +* *(appservice)* Switched appservice websockets from gorilla/websocket to + coder/websocket. +* *(bridgev2/matrix)* Fixed encryption key sharing not ignoring ghosts properly. +* *(crypto/attachments)* Fixed hash check when decrypting file streams. +* *(crypto)* Removed unnecessary `AlreadyShared` error in `ShareGroupSession`. + The function will now act as if it was successful instead. + +## v0.24.2 (2025-07-16) + +* *(bridgev2)* Added support for return values from portal event handlers. Note + that the return value will always be "queued" unless the event buffer is + disabled. +* *(bridgev2)* Added support for [MSC4144] per-message profile passthrough in + relay mode. +* *(bridgev2)* Added option to auto-reconnect logins after a certain period if + they hit an `UNKNOWN_ERROR` state. +* *(bridgev2)* Added analytics for event handler panics. +* *(bridgev2)* Changed new room creation to hardcode room v11 to avoid v12 rooms + being created before proper support for them can be added. +* *(bridgev2)* Changed queuing events to block instead of dropping events if the + buffer is full. +* *(bridgev2)* Fixed assumption that replies to unknown messages are cross-room. +* *(id)* Fixed server name validation not including ports correctly + (thanks to [@krombel] in [#392]). +* *(federation)* Fixed base64 algorithm in signature generation. +* *(event)* Fixed [MSC4144] fallbacks not being removed from edits. + +[@krombel]: https://github.com/krombel +[#392]: https://github.com/mautrix/go/pull/392 + +## v0.24.1 (2025-06-16) + +* *(commands)* Added framework for using reactions as buttons that execute + command handlers. +* *(client)* Added wrapper for `/relations` endpoints. +* *(client)* Added support for stable version of room summary endpoint. +* *(client)* Fixed parsing URL preview responses where width/height are strings. +* *(federation)* Fixed bugs in server auth. +* *(id)* Added utilities for validating server names. +* *(event)* Fixed incorrect empty `entity` field when sending hashed moderation + policy events. +* *(event)* Added [MSC4293] redact events field to member events. +* *(event)* Added support for fallbacks in [MSC4144] per-message profiles. +* *(format)* Added `MarkdownLink` and `MarkdownMention` utility functions for + generating properly escaped markdown. +* *(synapseadmin)* Added support for synchronous (v1) room delete endpoint. +* *(synapseadmin)* Changed `Client` struct to not embed the `mautrix.Client`. + This is a breaking change if you were relying on accessing non-admin functions + from the admin client. +* *(bridgev2/provisioning)* Fixed `/display_and_wait` not passing through errors + from the network connector properly. +* *(bridgev2/crypto)* Fixed encryption not working if the user's ID had the same + prefix as the bridge ghosts (e.g. `@whatsappbridgeuser:example.com` with a + `@whatsapp_` prefix). +* *(bridgev2)* Fixed portals not being saved after creating a DM portal from a + Matrix DM invite. +* *(bridgev2)* Added config option to determine whether cross-room replies + should be bridged. +* *(appservice)* Fixed `EnsureRegistered` not being called when sending a custom + member event for the controlled user. + +[MSC4293]: https://github.com/matrix-org/matrix-spec-proposals/pull/4293 + +## v0.24.0 (2025-05-16) + +* *(commands)* Added generic framework for implementing bot commands. +* *(client)* Added support for specifying maximum number of HTTP retries using + a context value instead of having to call `MakeFullRequest` manually. +* *(client,federation)* Added methods for fetching room directories. +* *(federation)* Added support for server side of request authentication. +* *(synapseadmin)* Added wrapper for the account suspension endpoint. +* *(format)* Added method for safely wrapping a string in markdown inline code. +* *(crypto)* Added method to import key backup without persisting to database, + to allow the client more control over the process. +* *(bridgev2)* Added viewing chat interface to signal when the user is viewing + a given chat. +* *(bridgev2)* Added option to pass through transaction ID from client when + sending messages to remote network. +* *(crypto)* Fixed unnecessary error log when decrypting dummy events used for + unwedging Olm sessions. +* *(crypto)* Fixed `forwarding_curve25519_key_chain` not being set consistently + when backing up keys. +* *(event)* Fixed marshaling legacy VoIP events with no version field. +* *(bridgev2)* Fixed disappearing message references not being deleted when the + portal is deleted. +* *(bridgev2)* Fixed read receipt bridging not ignoring fake message entries + and causing unnecessary error logs. + +## v0.23.3 (2025-04-16) + +* *(commands)* Added generic command processing framework for bots. +* *(client)* Added `allowed_room_ids` field to room summary responses + (thanks to [@nexy7574] in [#367]). +* *(bridgev2)* Added support for custom timeouts on outgoing messages which have + to wait for a remote echo. +* *(bridgev2)* Added automatic typing stop event if the ghost user had sent a + typing event before a message. +* *(bridgev2)* The saved management room is now cleared if the user leaves the + room, allowing the next DM to be automatically marked as a management room. +* *(bridge)* Removed deprecated fallback package for bridge statuses. + The status package is now only available under bridgev2. + +[#367]: https://github.com/mautrix/go/pull/367 + +## v0.23.2 (2025-03-16) + +* **Breaking change *(bridge)*** Removed legacy bridge module. +* **Breaking change *(event)*** Changed `m.federate` field in room create event + content to a pointer to allow detecting omitted values. +* *(bridgev2/commands)* Added `set-management-room` command to set a new + management room. +* *(bridgev2/portal)* Changed edit bridging to ignore remote edits if the + original sender on Matrix can't be puppeted. +* *(bridgv2)* Added config option to disable bridging `m.notice` messages. +* *(appservice/http)* Switched access token validation to use constant time + comparisons. +* *(event)* Added support for [MSC3765] rich text topics. +* *(event)* Added fields to policy list event contents for [MSC4204] and + [MSC4205]. +* *(client)* Added method for getting the content of a redacted event using + [MSC2815]. +* *(client)* Added methods for sending and updating [MSC4140] delayed events. +* *(client)* Added support for [MSC4222] in sync payloads. +* *(crypto/cryptohelper)* Switched to using `sqlite3-fk-wal` instead of plain + `sqlite3` by default. +* *(crypto/encryptolm)* Added generic method for encrypting to-device events. +* *(crypto/ssss)* Fixed panic if server-side key metadata is corrupted. +* *(crypto/sqlstore)* Fixed error when marking over 32 thousand device lists + as outdated on SQLite. + +[MSC2815]: https://github.com/matrix-org/matrix-spec-proposals/pull/2815 +[MSC3765]: https://github.com/matrix-org/matrix-spec-proposals/pull/3765 +[MSC4140]: https://github.com/matrix-org/matrix-spec-proposals/pull/4140 +[MSC4204]: https://github.com/matrix-org/matrix-spec-proposals/pull/4204 +[MSC4205]: https://github.com/matrix-org/matrix-spec-proposals/pull/4205 +[MSC4222]: https://github.com/matrix-org/matrix-spec-proposals/pull/4222 + +## v0.23.1 (2025-02-16) + +* *(client)* Added `FullStateEvent` method to get a state event including + metadata (using the `?format=event` query parameter). +* *(client)* Added wrapper method for [MSC4194]'s redact endpoint. +* *(pushrules)* Fixed content rules not considering word boundaries and being + case-sensitive. +* *(crypto)* Fixed bugs that would cause key exports to fail for no reason. +* *(crypto)* Deprecated `ResolveTrust` in favor of `ResolveTrustContext`. +* *(crypto)* Stopped accepting secret shares from unverified devices. +* **Breaking change *(crypto)*** Changed `GetAndVerifyLatestKeyBackupVersion` + to take an optional private key parameter. The method will now trust the + public key if it matches the provided private key even if there are no valid + signatures. +* **Breaking change *(crypto)*** Added context parameter to `IsDeviceTrusted`. + +[MSC4194]: https://github.com/matrix-org/matrix-spec-proposals/pull/4194 + +## v0.23.0 (2025-01-16) + +* **Breaking change *(client)*** Changed `JoinRoom` parameters to allow multiple + `via`s. +* **Breaking change *(bridgev2)*** Updated capability system. + * The return type of `NetworkAPI.GetCapabilities` is now different. + * Media type capabilities are enforced automatically by bridgev2. + * Capabilities are now sent to Matrix rooms using the + `com.beeper.room_features` state event. +* *(client)* Added `GetRoomSummary` to implement [MSC3266]. +* *(client)* Added support for arbitrary profile fields to implement [MSC4133] + (thanks to [@nexy7574] in [#337]). +* *(crypto)* Started storing olm message hashes to prevent decryption errors + if messages are repeated (e.g. if the app crashes right after decrypting). +* *(crypto)* Improved olm session unwedging to check when the last session was + created instead of only relying on an in-memory map. +* *(crypto/verificationhelper)* Fixed emoji verification not doing cross-signing + properly after a successful verification. +* *(bridgev2/config)* Moved MSC4190 flag from `appservice` to `encryption`. +* *(bridgev2/space)* Fixed failing to add rooms to spaces if the room create + call was made with a temporary context. +* *(bridgev2/commands)* Changed `help` command to hide commands which require + interfaces that aren't implemented by the network connector. +* *(bridgev2/matrixinterface)* Moved deterministic room ID generation to Matrix + connector. +* *(bridgev2)* Fixed service member state event not being set correctly when + creating a DM by inviting a ghost user. +* *(bridgev2)* Fixed `RemoteReactionSync` events replacing all reactions every + time instead of only changed ones. + +[MSC3266]: https://github.com/matrix-org/matrix-spec-proposals/pull/3266 +[MSC4133]: https://github.com/matrix-org/matrix-spec-proposals/pull/4133 +[@nexy7574]: https://github.com/nexy7574 +[#337]: https://github.com/mautrix/go/pull/337 + +## v0.22.1 (2024-12-16) + +* *(crypto)* Added automatic cleanup when there are too many olm sessions with + a single device. +* *(crypto)* Added helper for getting cached device list with cross-signing + status. +* *(crypto/verificationhelper)* Added interface for persisting the state of + in-progress verifications. +* *(client)* Added `GetMutualRooms` wrapper for [MSC2666]. +* *(client)* Switched `JoinRoom` to use the `via` query param instead of + `server_name` as per [MSC4156]. +* *(bridgev2/commands)* Fixed `pm` command not actually starting the chat. +* *(bridgev2/interface)* Added separate network API interface for starting + chats with a Matrix ghost user. This allows treating internal user IDs + differently than arbitrary user-input strings. +* *(bridgev2/crypto)* Added support for [MSC4190] + (thanks to [@onestacked] in [#288]). + +[MSC2666]: https://github.com/matrix-org/matrix-spec-proposals/pull/2666 +[MSC4156]: https://github.com/matrix-org/matrix-spec-proposals/pull/4156 +[MSC4190]: https://github.com/matrix-org/matrix-spec-proposals/pull/4190 +[#288]: https://github.com/mautrix/go/pull/288 + +## v0.22.0 (2024-11-16) + +* *(hicli)* Moved package into gomuks repo. +* *(bridgev2/commands)* Fixed cookie unescaping in login commands. +* *(bridgev2/portal)* Added special `DefaultChatName` constant to explicitly + reset portal names to the default (based on members). +* *(bridgev2/config)* Added options to disable room tag bridging. +* *(bridgev2/database)* Fixed reaction queries not including portal receiver. +* *(appservice)* Updated [MSC2409] stable registration field name from + `push_ephemeral` to `receive_ephemeral`. Homeserver admins must update + existing registrations manually. +* *(format)* Added support for `img` tags. +* *(format/mdext)* Added goldmark extensions for Matrix math and custom emojis. +* *(event/reply)* Removed support for generating reply fallbacks ([MSC2781]). +* *(pushrules)* Added support for `sender_notification_permission` condition + kind (used for `@room` mentions). +* *(crypto)* Added support for `json.RawMessage` in `EncryptMegolmEvent`. +* *(mediaproxy)* Added `GetMediaResponseCallback` and `GetMediaResponseFile` + to write proxied data directly to http response or temp file instead of + having to use an `io.Reader`. +* *(mediaproxy)* Dropped support for legacy media download endpoints. +* *(mediaproxy,bridgev2)* Made interface pass through query parameters. + +[MSC2781]: https://github.com/matrix-org/matrix-spec-proposals/pull/2781 + +## v0.21.1 (2024-10-16) + +* *(bridgev2)* Added more features and fixed bugs. +* *(hicli)* Added more features and fixed bugs. +* *(appservice)* Removed TLS support. A reverse proxy should be used if TLS + is needed. +* *(format/mdext)* Added goldmark extension to fix indented paragraphs when + disabling indented code block parser. +* *(event)* Added `Has` method for `Mentions`. +* *(event)* Added basic support for the unstable version of polls. + +## v0.21.0 (2024-09-16) + +* **Breaking change *(client)*** Dropped support for unauthenticated media. + Matrix v1.11 support is now required from the homeserver, although it's not + enforced using `/versions` as some servers don't advertise it. +* *(bridgev2)* Added more features and fixed bugs. +* *(appservice,crypto)* Added support for using MSC3202 for appservice + encryption. +* *(crypto/olm)* Made everything into an interface to allow side-by-side + testing of libolm and goolm, as well as potentially support vodozemac + in the future. +* *(client)* Fixed requests being retried even after context is canceled. +* *(client)* Added option to move `/sync` request logs to trace level. +* *(error)* Added `Write` and `WithMessage` helpers to `RespError` to make it + easier to use on servers. +* *(event)* Fixed `org.matrix.msc1767.audio` field allowing omitting the + duration and waveform. +* *(id)* Changed `MatrixURI` methods to not panic if the receiver is nil. +* *(federation)* Added limit to response size when fetching `.well-known` files. + +## v0.20.0 (2024-08-16) + +* Bumped minimum Go version to 1.22. +* *(bridgev2)* Added more features and fixed bugs. +* *(event)* Added types for [MSC4144]: Per-message profiles. +* *(federation)* Added implementation of server name resolution and a basic + client for making federation requests. +* *(crypto/ssss)* Changed recovery key/passphrase verify functions to take the + key ID as a parameter to ensure it's correctly set even if the key metadata + wasn't fetched via `GetKeyData`. +* *(format/mdext)* Added goldmark extensions for single-character bold, italic + and strikethrough parsing (as in `*foo*` -> **foo**, `_foo_` -> _foo_ and + `~foo~` -> ~~foo~~) +* *(format)* Changed `RenderMarkdown` et al to always include `m.mentions` in + returned content. The mention list is filled with matrix.to URLs from the + input by default. + +[MSC4144]: https://github.com/matrix-org/matrix-spec-proposals/pull/4144 + +## v0.19.0 (2024-07-16) + +* Renamed `master` branch to `main`. +* *(bridgev2)* Added more features. +* *(crypto)* Fixed bug with copying `m.relates_to` from wire content to + decrypted content. +* *(mediaproxy)* Added module for implementing simple media repos that proxy + requests elsewhere. +* *(client)* Changed `Members()` to automatically parse event content for all + returned events. +* *(bridge)* Added `/register` call if `/versions` fails with `M_FORBIDDEN`. +* *(crypto)* Fixed `DecryptMegolmEvent` sometimes calling database without + transaction by using the non-context version of `ResolveTrust`. +* *(crypto/attachment)* Implemented `io.Seeker` in `EncryptStream` to allow + using it in retriable HTTP requests. +* *(event)* Added helper method to add user ID to a `Mentions` object. +* *(event)* Fixed default power level for invites + (thanks to [@rudis] in [#250]). +* *(client)* Fixed incorrect warning log in `State()` when state store returns + no error (thanks to [@rudis] in [#249]). +* *(crypto/verificationhelper)* Fixed deadlock when ignoring unknown + cancellation events (thanks to [@rudis] in [#247]). + +[@rudis]: https://github.com/rudis +[#250]: https://github.com/mautrix/go/pull/250 +[#249]: https://github.com/mautrix/go/pull/249 +[#247]: https://github.com/mautrix/go/pull/247 + +### beta.1 (2024-06-16) + +* *(bridgev2)* Added experimental high-level bridge framework. +* *(hicli)* Added experimental high-level client framework. +* **Slightly breaking changes** + * *(crypto)* Added room ID and first known index parameters to + `SessionReceived` callback. + * *(crypto)* Changed `ImportRoomKeyFromBackup` to return the imported + session. + * *(client)* Added `error` parameter to `ResponseHook`. + * *(client)* Changed `Download` to return entire response instead of just an + `io.Reader`. +* *(crypto)* Changed initial olm device sharing to save keys before sharing to + ensure keys aren't accidentally regenerated in case the request fails. +* *(crypto)* Changed `EncryptMegolmEvent` and `ShareGroupSession` to return + more errors instead of only logging and ignoring them. +* *(crypto)* Added option to completely disable megolm ratchet tracking. + * The tracking is meant for bots and bridges which may want to delete old + keys, but for normal clients it's just unnecessary overhead. +* *(crypto)* Changed Megolm session storage methods in `Store` to not take + sender key as parameter. + * This causes a breaking change to the layout of the `MemoryStore` struct. + Using MemoryStore in production is not recommended. +* *(crypto)* Changed `DecryptMegolmEvent` to copy `m.relates_to` in the raw + content too instead of only in the parsed struct. +* *(crypto)* Exported function to parse megolm message index from raw + ciphertext bytes. +* *(crypto/sqlstore)* Fixed schema of `crypto_secrets` table to include + account ID. +* *(crypto/verificationhelper)* Fixed more bugs. +* *(client)* Added `UpdateRequestOnRetry` hook which is called immediately + before retrying a normal HTTP request. +* *(client)* Added support for MSC3916 media download endpoint. + * Support is automatically detected from spec versions. The `SpecVersions` + property can either be filled manually, or `Versions` can be called to + automatically populate the field with the response. +* *(event)* Added constants for known room versions. + +## v0.18.1 (2024-04-16) + +* *(format)* Added a `context.Context` field to HTMLParser's Context struct. +* *(bridge)* Added support for handling join rules, knocks, invites and bans + (thanks to [@maltee1] in [#193] and [#204]). +* *(crypto)* Changed forwarded room key handling to only accept keys with a + lower first known index than the existing session if there is one. +* *(crypto)* Changed key backup restore to assume own device list is up to date + to avoid re-requesting device list for every deleted device that has signed + key backup. +* *(crypto)* Fixed memory cache not being invalidated when storing own + cross-signing keys + +[@maltee1]: https://github.com/maltee1 +[#193]: https://github.com/mautrix/go/pull/193 +[#204]: https://github.com/mautrix/go/pull/204 + +## v0.18.0 (2024-03-16) + +* **Breaking change *(client, bridge, appservice)*** Dropped support for + maulogger. Only zerolog loggers are now provided by default. +* *(bridge)* Fixed upload size limit not having a default if the server + returned no value. +* *(synapseadmin)* Added wrappers for some room and user admin APIs. + (thanks to [@grvn-ht] in [#181]). +* *(crypto/verificationhelper)* Fixed bugs. +* *(crypto)* Fixed key backup uploading doing too much base64. +* *(crypto)* Changed `EncryptMegolmEvent` to return an error if persisting the + megolm session fails. This ensures that database errors won't cause messages + to be sent with duplicate indexes. +* *(crypto)* Changed `GetOrRequestSecret` to use a callback instead of returning + the value directly. This allows validating the value in order to ignore + invalid secrets. +* *(id)* Added `ParseCommonIdentifier` function to parse any Matrix identifier + in the [Common Identifier Format]. +* *(federation)* Added simple key server that passes the federation tester. + +[@grvn-ht]: https://github.com/grvn-ht +[#181]: https://github.com/mautrix/go/pull/181 +[Common Identifier Format]: https://spec.matrix.org/v1.9/appendices/#common-identifier-format + +### beta.1 (2024-02-16) + +* Bumped minimum Go version to 1.21. +* *(bridge)* Bumped minimum Matrix spec version to v1.4. +* **Breaking change *(crypto)*** Deleted old half-broken interactive + verification code and replaced it with a new `verificationhelper`. + * The new verification helper is still experimental. + * Both QR and emoji verification are supported (in theory). +* *(crypto)* Added support for server-side key backup. +* *(crypto)* Added support for receiving and sending secrets like cross-signing + private keys via secret sharing. +* *(crypto)* Added support for tracking which devices megolm sessions were + initially shared to, and allowing re-sharing the keys to those sessions. +* *(client)* Changed cross-signing key upload method to accept a callback for + user-interactive auth instead of only hardcoding password support. +* *(appservice)* Dropped support for legacy non-prefixed appservice paths + (e.g. `/transactions` instead of `/_matrix/app/v1/transactions`). +* *(appservice)* Dropped support for legacy `access_token` authorization in + appservice endpoints. +* *(bridge)* Fixed `RawArgs` field in command events of command state callbacks. +* *(appservice)* Added `CreateFull` helper function for creating an `AppService` + instance with all the mandatory fields set. + +## v0.17.0 (2024-01-16) + +* **Breaking change *(bridge)*** Added raw event to portal membership handling + functions. +* **Breaking change *(everything)*** Added context parameters to all functions + (started by [@recht] in [#144]). +* **Breaking change *(client)*** Moved event source from sync event handler + function parameters to the `Mautrix.EventSource` field inside the event + struct. +* **Breaking change *(client)*** Moved `EventSource` to `event.Source`. +* *(client)* Removed deprecated `OldEventIgnorer`. The non-deprecated version + (`Client.DontProcessOldEvents`) is still available. +* *(crypto)* Added experimental pure Go Olm implementation to replace libolm + (thanks to [@DerLukas15] in [#106]). + * You can use the `goolm` build tag to the new implementation. +* *(bridge)* Added context parameter for bridge command events. +* *(bridge)* Added method to allow custom validation for the entire config. +* *(client)* Changed default syncer to not drop unknown events. + * The syncer will still drop known events if parsing the content fails. + * The behavior can be changed by changing the `ParseErrorHandler` function. +* *(crypto)* Fixed some places using math/rand instead of crypto/rand. + +[@DerLukas15]: https://github.com/DerLukas15 +[@recht]: https://github.com/recht +[#106]: https://github.com/mautrix/go/pull/106 +[#144]: https://github.com/mautrix/go/pull/144 + +## v0.16.2 (2023-11-16) + +* *(event)* Added `Redacts` field to `RedactionEventContent` for room v11+. +* *(event)* Added `ReverseTextToHTML` which reverses the changes made by + `TextToHTML` (i.e. unescapes HTML characters and replaces `
` with `\n`). +* *(bridge)* Added global zerologger to ensure all logs go through the bridge + logger. +* *(bridge)* Changed encryption error messages to be sent in a thread if the + message that failed to decrypt was in a thread. + +## v0.16.1 (2023-09-16) + +* **Breaking change *(id)*** Updated user ID localpart encoding to not encode + `+` as per [MSC4009]. +* *(bridge)* Added bridge utility to handle double puppeting logins. + * The utility supports automatic logins with all three current methods + (shared secret, legacy appservice, new appservice). +* *(appservice)* Added warning logs and timeout on appservice event handling. + * Defaults to warning after 30 seconds and timeout 15 minutes after that. + * Timeouts can be adjusted or disabled by setting `ExecSync` variables in the + `EventProcessor`. +* *(crypto/olm)* Added `PkDecryption` wrapper. + +[MSC4009]: https://github.com/matrix-org/matrix-spec-proposals/pull/4009 + +## v0.16.0 (2023-08-16) + +* Bumped minimum Go version to 1.20. +* **Breaking change *(util)*** Moved package to [go.mau.fi/util](https://go.mau.fi/util/) +* *(event)* Removed MSC2716 `historical` field in the `m.room.power_levels` + event content struct. +* *(bridge)* Added `--version-json` flag to print bridge version info as JSON. +* *(appservice)* Added option to use custom transaction handler for websocket mode. + +## v0.15.4 (2023-07-16) + +* *(client)* Deprecated MSC2716 methods and added new Beeper-specific batch + send methods, as upstream MSC2716 support has been abandoned. +* *(client)* Added proper error handling and automatic retries to media + downloads. +* *(crypto, bridge)* Added option to remove all keys that were received before + the automatic ratcheting was implemented (in v0.15.1). +* *(dbutil)* Added `JSON` utility for writing/reading arbitrary JSON objects to + the db conveniently without manually de/serializing. + +## v0.15.3 (2023-06-16) + +* *(synapseadmin)* Added wrappers for some Synapse admin API endpoints. +* *(pushrules)* Implemented new `event_property_is` and `event_property_contains` + push rule condition kinds as per MSC3758 and MSC3966. +* *(bridge)* Moved websocket code from mautrix-imessage to enable all bridges + to use appservice websockets easily. +* *(bridge)* Added retrying for appservice pings. +* *(types)* Removed unstable field for MSC3952 (intentional mentions). +* *(client)* Deprecated `OldEventIgnorer` and added `Client.DontProcessOldEvents` + to replace it. +* *(client)* Added `MoveInviteState` sync handler for moving state events in + the invite section of sync inside the invite event itself. +* *(crypto)* Added option to not rotate keys when devices change. +* *(crypto)* Added additional duplicate message index check if decryption fails + because the keys had been ratcheted forward. +* *(client)* Stabilized support for asynchronous uploads. + * `UnstableCreateMXC` and `UnstableUploadAsync` were renamed to `CreateMXC` + and `UploadAsync` respectively. +* *(util/dbutil)* Added option to use a separate database connection pool for + read-only transactions. + * This is mostly meant for SQLite and it enables read-only transactions that + don't lock the database, even when normal transactions are configured to + acquire a write lock immediately. +* *(util/dbutil)* Enabled caller info in zerolog by default. + +## v0.15.2 (2023-05-16) + +* *(client)* Changed member-fetching methods to clear existing member info in + state store. +* *(client)* Added support for inserting mautrix-go commit hash into default + user agent at compile time. +* *(bridge)* Fixed bridge bot intent not having state store set. +* *(client)* Fixed `RespError` marshaling mutating the `ExtraData` map and + potentially causing panics. +* *(util/dbutil)* Added `DoTxn` method for an easier way to manage database + transactions. +* *(util)* Added a zerolog `CallerMarshalFunc` implementation that includes the + function name. +* *(bridge)* Added error reply to encrypted messages if the bridge isn't + configured to do encryption. + +## v0.15.1 (2023-04-16) + +* *(crypto, bridge)* Added options to automatically ratchet/delete megolm + sessions to minimize access to old messages. +* *(pushrules)* Added method to get entire push rule that matched (instead of + only the list of actions). +* *(pushrules)* Deprecated `NotifySpecified` as there's no reason to read it. +* *(crypto)* Changed `max_age` column in `crypto_megolm_inbound_session` table + to be milliseconds instead of nanoseconds. +* *(util)* Added method for iterating `RingBuffer`. +* *(crypto/cryptohelper)* Changed decryption errors to request session from all + own devices in addition to the sender, instead of only asking the sender. +* *(sqlstatestore)* Fixed `FindSharedRooms` throwing an error when using from + a non-bridge context. +* *(client)* Optimized `AccountDataSyncStore` to not resend save requests if + the sync token didn't change. +* *(types)* Added `Clone()` method for `PowerLevelEventContent`. + +## v0.15.0 (2023-03-16) + +### beta.3 (2023-03-15) + +* **Breaking change *(appservice)*** Removed `Load()` and `AppService.Init()` + functions. The struct should just be created with `Create()` and the relevant + fields should be filled manually. +* **Breaking change *(appservice)*** Removed public `HomeserverURL` field and + replaced it with a `SetHomeserverURL` method. +* *(appservice)* Added support for unix sockets for homeserver URL and + appservice HTTP server. +* *(client)* Changed request logging to log durations as floats instead of + strings (using zerolog's `Dur()`, so the exact output can be configured). +* *(bridge)* Changed zerolog to use nanosecond precision timestamps. +* *(crypto)* Added message index to log after encrypting/decrypting megolm + events, and when failing to decrypt due to duplicate index. +* *(sqlstatestore)* Fixed warning log for rooms that don't have encryption + enabled. + +### beta.2 (2023-03-02) + +* *(bridge)* Fixed building with `nocrypto` tag. +* *(bridge)* Fixed legacy logging config migration not disabling file writer + when `file_name_format` was empty. +* *(bridge)* Added option to require room power level to run commands. +* *(event)* Added structs for [MSC3952]: Intentional Mentions. +* *(util/variationselector)* Added `FullyQualify` method to add necessary emoji + variation selectors without adding all possible ones. + +[MSC3952]: https://github.com/matrix-org/matrix-spec-proposals/pull/3952 + +### beta.1 (2023-02-24) + +* Bumped minimum Go version to 1.19. +* **Breaking changes** + * *(all)* Switched to zerolog for logging. + * The `Client` and `Bridge` structs still include a legacy logger for + backwards compatibility. + * *(client, appservice)* Moved `SQLStateStore` from appservice module to the + top-level (client) module. + * *(client, appservice)* Removed unused `Typing` map in `SQLStateStore`. + * *(client)* Removed unused `SaveRoom` and `LoadRoom` methods in `Storer`. + * *(client, appservice)* Removed deprecated `SendVideo` and `SendImage` methods. + * *(client)* Replaced `AppServiceUserID` field with `SetAppServiceUserID` boolean. + The `UserID` field is used as the value for the query param. + * *(crypto)* Renamed `GobStore` to `MemoryStore` and removed the file saving + features. The data can still be persisted, but the persistence part must be + implemented separately. + * *(crypto)* Removed deprecated `DeviceIdentity` alias + (renamed to `id.Device` long ago). + * *(client)* Removed `Stringifable` interface as it's the same as `fmt.Stringer`. +* *(client)* Renamed `Storer` interface to `SyncStore`. A type alias exists for + backwards-compatibility. +* *(crypto/cryptohelper)* Added package for a simplified crypto interface for clients. +* *(example)* Added e2ee support to example using crypto helper. +* *(client)* Changed default syncer to stop syncing on `M_UNKNOWN_TOKEN` errors. + +## v0.14.0 (2023-02-16) + +* **Breaking change *(format)*** Refactored the HTML parser `Context` to have + more data. +* *(id)* Fixed escaping path components when forming matrix.to URLs + or `matrix:` URIs. +* *(bridge)* Bumped default timeouts for decrypting incoming messages. +* *(bridge)* Added `RawArgs` to commands to allow accessing non-split input. +* *(bridge)* Added `ReplyAdvanced` to commands to allow setting markdown + settings. +* *(event)* Added `notifications` key to `PowerLevelEventContent`. +* *(event)* Changed `SetEdit` to cut off edit fallback if the message is long. +* *(util)* Added `SyncMap` as a simple generic wrapper for a map with a mutex. +* *(util)* Added `ReturnableOnce` as a wrapper for `sync.Once` with a return + value. + +## v0.13.0 (2023-01-16) + +* **Breaking change:** Removed `IsTyping` and `SetTyping` in `appservice.StateStore` + and removed the `TypingStateStore` struct implementing those methods. +* **Breaking change:** Removed legacy fields in Beeper MSS events. +* Added knocked rooms to sync response structs. +* Added wrapper for `/timestamp_to_event` endpoint added in Matrix v1.6. +* Fixed MSC3870 uploads not failing properly after using up the max retry count. +* Fixed parsing non-positive ordered list start positions in HTML parser. + +## v0.12.4 (2022-12-16) + +* Added `SendReceipt` to support private read receipts and thread receipts in + the same function. `MarkReadWithContent` is now deprecated. +* Changed media download methods to return errors if the server returns a + non-2xx status code. +* Removed legacy `sql_store_upgrade.Upgrade` method. Using `store.DB.Upgrade()` + after `NewSQLCryptoStore(...)` is recommended instead (the bridge module does + this automatically). +* Added missing `suggested` field to `m.space.child` content struct. +* Added `device_unused_fallback_key_types` to `/sync` response and appservice + transaction structs. +* Changed `ReqSetReadMarkers` to omit empty fields. +* Changed bridge configs to force `sqlite3-fk-wal` instead of `sqlite3`. +* Updated bridge helper to close database connection when stopping. +* Fixed read receipt and account data endpoints sending `null` instead of an + empty object as the body when content isn't provided. + +## v0.12.3 (2022-11-16) + +* **Breaking change:** Added logging for row iteration in the dbutil package. + This changes the return type of `Query` methods from `*sql.Rows` to a new + `dbutil.Rows` interface. +* Added flag to disable wrapping database upgrades in a transaction (e.g. to + allow setting `PRAGMA`s for advanced table mutations on SQLite). +* Deprecated `MessageEventContent.GetReplyTo` in favor of directly using + `RelatesTo.GetReplyTo`. RelatesTo methods are nil-safe, so checking if + RelatesTo is nil is not necessary for using those methods. +* Added wrapper for space hierarchyendpoint (thanks to [@mgcm] in [#100]). +* Added bridge config option to handle transactions asynchronously. +* Added separate channels for to-device events in appservice transaction + handler to avoid blocking to-device events behind normal events. +* Added `RelatesTo.GetNonFallbackReplyTo` utility method to get the reply event + ID, unless the reply is a thread fallback. +* Added `event.TextToHTML` as an utility method to HTML-escape a string and + replace newlines with `
`. +* Added check to bridge encryption helper to make sure the e2ee keys are still + on the server. Synapse is known to sometimes lose keys randomly. +* Changed bridge crypto syncer to crash on `M_UNKNOWN_TOKEN` errors instead of + retrying forever pointlessly. +* Fixed verifying signatures of fallback one-time keys. + +[@mgcm]: https://github.com/mgcm +[#100]: https://github.com/mautrix/go/pull/100 + +## v0.12.2 (2022-10-16) + +* Added utility method to redact bridge commands. +* Added thread ID field to read receipts to match Matrix v1.4 changes. +* Added automatic fetching of media repo config at bridge startup to make it + easier for bridges to check homeserver media size limits. +* Added wrapper for the `/register/available` endpoint. +* Added custom user agent to all requests mautrix-go makes. The value can be + customized by changing the `DefaultUserAgent` variable. +* Implemented [MSC3664], [MSC3862] and [MSC3873] in the push rule evaluator. +* Added workaround for potential race conditions in OTK uploads when using + appservice encryption ([MSC3202]). +* Fixed generating registrations to use `.+` instead of `[0-9]+` in the + username regex. +* Fixed panic in megolm session listing methods if the store contains withheld + key entries. +* Fixed missing header in bridge command help messages. + +[MSC3664]: https://github.com/matrix-org/matrix-spec-proposals/pull/3664 +[MSC3862]: https://github.com/matrix-org/matrix-spec-proposals/pull/3862 +[MSC3873]: https://github.com/matrix-org/matrix-spec-proposals/pull/3873 + +## v0.12.1 (2022-09-16) + +* Bumped minimum Go version to 1.18. +* Added `omitempty` for a bunch of fields in response structs to make them more + usable for server implementations. +* Added `util.RandomToken` to generate GitHub-style access tokens with checksums. +* Added utilities to call the push gateway API. +* Added `unread_notifications` and [MSC2654] `unread_count` fields to /sync + response structs. +* Implemented [MSC3870] for uploading and downloading media directly to/from an + external media storage like S3. +* Fixed dbutil database ownership checks on SQLite. +* Fixed typo in unauthorized encryption key withheld code + (`m.unauthorized` -> `m.unauthorised`). +* Fixed [MSC2409] support to have a separate field for to-device events. + +[MSC2654]: https://github.com/matrix-org/matrix-spec-proposals/pull/2654 +[MSC3870]: https://github.com/matrix-org/matrix-spec-proposals/pull/3870 + +## v0.12.0 (2022-08-16) + +* **Breaking change:** Switched `Client.UserTyping` to take a `time.Duration` + instead of raw `int64` milliseconds. +* **Breaking change:** Removed custom reply relation type and switched to using + the wire format (nesting in `m.in_reply_to`). +* Added device ID to appservice OTK count map to match updated [MSC3202]. + This is also a breaking change, but the previous incorrect behavior wasn't + implemented by anything other than mautrix-syncproxy/imessage. +* (There are probably other breaking changes too). +* Added database utility and schema upgrade framework + * Originally from mautrix-whatsapp, but usable for non-bridges too + * Includes connection wrapper to log query durations and mutate queries for + SQLite compatibility (replacing `$x` with `?x`). +* Added bridge utilities similar to mautrix-python. Currently includes: + * Crypto helper + * Startup flow + * Command handling and some standard commands + * Double puppeting things + * Generic parts of config, basic config validation + * Appservice SQL state store +* Added alternative markdown spoiler parsing extension that doesn't support + reasons, but works better otherwise. +* Added Discord underline markdown parsing extension (`_foo_` -> foo). +* Added support for parsing spoilers and color tags in the HTML parser. +* Added support for mutating plain text nodes in the HTML parser. +* Added room version field to the create room request struct. +* Added empty JSON object as default request body for all non-GET requests. +* Added wrapper for `/capabilities` endpoint. +* Added `omitempty` markers for lots of structs to make the structs easier to + use on the server side too. +* Added support for registering to-device event handlers via the default + Syncer's `OnEvent` and `OnEventType` methods. +* Fixed `CreateEventContent` using the wrong field name for the room version + field. +* Fixed `StopSync` not immediately cancelling the sync loop if it was sleeping + after a failed sync. +* Fixed `GetAvatarURL` always returning the current user's avatar instead of + the specified user's avatar (thanks to [@nightmared] in [#83]). +* Improved request logging and added new log when a request finishes. +* Crypto store improvements: + * Deleted devices are now kept in the database. + * Made ValidateMessageIndex atomic. +* Moved `appservice.RandomString` to the `util` package and made it use + `crypto/rand` instead of `math/rand`. +* Significantly improved cross-signing validation code. + * There are now more options for required trust levels, + e.g. you can set `SendKeysMinTrust` to `id.TrustStateCrossSignedTOFU` + to trust the first cross-signing master key seen and require all devices + to be signed by that key. + * Trust state of incoming messages is automatically resolved and stored in + `evt.Mautrix.TrustState`. This can be used to reject incoming messages from + untrusted devices. + +[@nightmared]: https://github.com/nightmared +[#83]: https://github.com/mautrix/go/pull/83 + +## v0.11.1 (2023-01-15) + +* Fixed parsing non-positive ordered list start positions in HTML parser + (backport of the same fix in v0.13.0). + +## v0.11.0 (2022-05-16) + +* Bumped minimum Go version to 1.17. +* Switched from `/r0` to `/v3` paths everywhere. + * The new `v3` paths are implemented since Synapse 1.48, Dendrite 0.6.5, and + Conduit 0.4.0. Servers older than these are no longer supported. +* Switched from blackfriday to goldmark for markdown parsing in the `format` + module and added spoiler syntax. +* Added `EncryptInPlace` and `DecryptInPlace` methods for attachment encryption. + In most cases the plain/ciphertext is not necessary after en/decryption, so + the old `Encrypt` and `Decrypt` are deprecated. +* Added wrapper for `/rooms/.../aliases`. +* Added utility for adding/removing emoji variation selectors to match + recommendations on reactions in Matrix. +* Added support for async media uploads ([MSC2246]). +* Added automatic sleep when receiving 429 error + (thanks to [@ownaginatious] in [#44]). +* Added support for parsing spec version numbers from the `/versions` endpoint. +* Removed unstable prefixed constant used for appservice login. +* Fixed URL encoding not working correctly in some cases. + +[MSC2246]: https://github.com/matrix-org/matrix-spec-proposals/pull/2246 +[@ownaginatious]: https://github.com/ownaginatious +[#44]: https://github.com/mautrix/go/pull/44 + +## v0.10.12 (2022-03-16) + +* Added option to use a different `Client` to send invites in + `IntentAPI.EnsureJoined`. +* Changed `MessageEventContent` struct to omit empty `msgtype`s in the output + JSON, as sticker events shouldn't have that field. +* Fixed deserializing the `thumbnail_file` field in `FileInfo`. +* Fixed bug that broke `SQLCryptoStore.FindDeviceByKey`. + +## v0.10.11 (2022-02-16) + +* Added automatic updating of state store from `IntentAPI` calls. +* Added option to ignore cache in `IntentAPI.EnsureJoined`. +* Added `GetURLPreview` as a wrapper for the `/preview_url` media repo endpoint. +* Moved base58 module inline to avoid pulling in btcd as a dependency. + +## v0.10.10 (2022-01-16) + +* Added event types and content structs for server ACLs and moderation policy + lists (thanks to [@qua3k] in [#59] and [#60]). +* Added optional parameter to `Client.LeaveRoom` to pass a `reason` field. + +[#59]: https://github.com/mautrix/go/pull/59 +[#60]: https://github.com/mautrix/go/pull/60 + +## v0.10.9 (2022-01-04) + +* **Breaking change:** Changed `Messages()` to take a filter as a parameter + instead of using the syncer's filter (thanks to [@qua3k] in [#55] and [#56]). + * The previous filter behavior was completely broken, as it sent a whole + filter instead of just a RoomEventFilter. + * Passing `nil` as the filter is fine and will disable filtering + (which is equivalent to what it did before with the invalid filter). +* Added `Context()` wrapper for the `/context` API (thanks to [@qua3k] in [#54]). +* Added utility for converting media files with ffmpeg. + +[#54]: https://github.com/mautrix/go/pull/54 +[#55]: https://github.com/mautrix/go/pull/55 +[#56]: https://github.com/mautrix/go/pull/56 +[@qua3k]: https://github.com/qua3k + +## v0.10.8 (2021-12-30) + +* Added `OlmSession.Describe()` to wrap `olm_session_describe`. +* Added trace logs to log olm session descriptions when encrypting/decrypting + to-device messages. +* Added space event types and content structs. +* Added support for power level content override field in `CreateRoom`. +* Fixed ordering of olm sessions which would cause an old session to be used in + some cases even after a client created a new session. + +## v0.10.7 (2021-12-16) + +* Changed `Client.RedactEvent` to allow arbitrary fields in redaction request. + +## v0.10.5 (2021-12-06) + +* Fixed websocket disconnection not clearing all pending requests. +* Added `OlmMachine.SendRoomKeyRequest` as a more direct way of sending room + key requests. +* Added automatic Olm session recreation if an incoming message fails to decrypt. +* Changed `Login` to only omit request content from logs if there's a password + or token (appservice logins don't have sensitive content). + +## v0.10.4 (2021-11-25) + +* Added `reason` field to invite and unban requests + (thanks to [@ptman] in [#48]). +* Fixed `AppService.HasWebsocket()` returning `true` even after websocket has + disconnected. + +[@ptman]: https://github.com/ptman +[#48]: https://github.com/mautrix/go/pull/48 + +## v0.10.3 (2021-11-18) + +* Added logs about incoming appservice transactions. +* Added support for message send checkpoints (as HTTP requests, similar to the + bridge state reporting system). + +## v0.10.2 (2021-11-15) + +* Added utility method for finding the first supported login flow matching any + of the given types. +* Updated registering appservice ghosts to use `inhibit_login` flag to prevent + lots of unnecessary access tokens from being created. + * If you want to log in as an appservice ghost, you should use [MSC2778]'s + appservice login (e.g. like [mautrix-whatsapp does for e2be](https://github.com/mautrix/whatsapp/blob/v0.2.1/crypto.go#L143-L149)). + +## v0.10.1 (2021-11-05) + +* Removed direct dependency on `pq` + * In order to use some more efficient queries on postgres, you must set + `crypto.PostgresArrayWrapper = pq.Array` if you want to use both postgres + and e2ee. +* Added temporary hack to ignore state events with the MSC2716 historical flag + (to be removed after [matrix-org/synapse#11265] is merged) +* Added received transaction acknowledgements for websocket appservice + transactions. +* Added automatic fallback to move `prev_content` from top level to the + standard location inside `unsigned`. + +[matrix-org/synapse#11265]: https://github.com/matrix-org/synapse/pull/11265 + +## v0.9.31 (2021-10-27) + +* Added `SetEdit` utility function for `MessageEventContent`. + +## v0.9.30 (2021-10-26) + +* Added wrapper for [MSC2716]'s `/batch_send` endpoint. +* Added `MarshalJSON` method for `Event` struct to prevent empty unsigned + structs from being included in the JSON. + +[MSC2716]: https://github.com/matrix-org/matrix-spec-proposals/pull/2716 + +## v0.9.29 (2021-09-30) + +* Added `client.State` method to get full room state. +* Added bridge info structs and event types ([MSC2346]). +* Made response handling more customizable. +* Fixed type of `AuthType` constants. + +[MSC2346]: https://github.com/matrix-org/matrix-spec-proposals/pull/2346 + +## v0.9.28 (2021-09-30) + +* Added `X-Mautrix-Process-ID` to appservice websocket headers to help debug + issues where multiple instances are connecting to the server at the same time. + +## v0.9.27 (2021-09-23) + +* Fixed Go 1.14 compatibility (broken in v0.9.25). +* Added GitHub actions CI to build, test and check formatting on Go 1.14-1.17. + +## v0.9.26 (2021-09-21) + +* Added default no-op logger to `Client` in order to prevent panic when the + application doesn't set a logger. + +## v0.9.25 (2021-09-19) + +* Disabled logging request JSON for sensitive requests like `/login`, + `/register` and other UIA endpoints. Logging can still be enabled by + setting `MAUTRIX_LOG_SENSITIVE_CONTENT` to `yes`. +* Added option to store new homeserver URL from `/login` response well-known data. +* Added option to stream big sync responses via disk to maybe reduce memory usage. +* Fixed trailing slashes in homeserver URL breaking all requests. + +## v0.9.24 (2021-09-03) + +* Added write deadline for appservice websocket connection. + +## v0.9.23 (2021-08-31) + +* Fixed storing e2ee key withheld events in the SQL store. + +## v0.9.22 (2021-08-30) + +* Updated appservice handler to cache multiple recent transaction IDs + instead of only the most recent one. + +## v0.9.21 (2021-08-25) + +* Added liveness and readiness endpoints to appservices. + * The endpoints are the same as mautrix-python: + `/_matrix/mau/live` and `/_matrix/mau/ready` + * Liveness always returns 200 and an empty JSON object by default, + but it can be turned off by setting `appservice.Live` to `false`. + * Readiness defaults to returning 500, and it can be switched to 200 + by setting `appservice.Ready` to `true`. + +## v0.9.20 (2021-08-19) + +* Added crypto store migration for converting all `VARCHAR(255)` columns + to `TEXT` in Postgres databases. + +## v0.9.19 (2021-08-17) + +* Fixed HTML parser outputting two newlines after paragraph tags. + +## v0.9.18 (2021-08-16) + +* Added new `BuildURL` method that does the same as `Client.BuildBaseURL` + but without requiring the `Client` instance. + +## v0.9.17 (2021-07-25) + +* Fixed handling OTK counts and device lists coming in through the appservice + transaction websocket. +* Updated OlmMachine to ignore OTK counts intended for other devices. + +## v0.9.15 (2021-07-16) + +* Added support for [MSC3202] and the to-device part of [MSC2409] in the + appservice package. +* Added support for sending commands through appservice websocket. +* Changed error message JSON field name in appservice error responses to + conform with standard Matrix errors (`message` -> `error`). + +[MSC3202]: https://github.com/matrix-org/matrix-spec-proposals/pull/3202 + +## v0.9.14 (2021-06-17) + +* Added default implementation of `PillConverter` in HTML parser utility. + +## v0.9.13 (2021-06-15) + +* Added support for parsing and generating encoded matrix.to URLs and `matrix:` URIs ([MSC2312](https://github.com/matrix-org/matrix-doc/pull/2312)). +* Updated HTML parser to use new URI parser for parsing user/room pills. + +## v0.9.12 (2021-05-18) + +* Added new method for sending custom data with read receipts + (not currently a part of the spec). + +## v0.9.11 (2021-05-12) + +* Improved debug log for unsupported event types. +* Added VoIP events to GuessClass. +* Added support for parsing strings in VoIP event version field. + +## v0.9.10 (2021-04-29) + +* Fixed `format.RenderMarkdown()` still allowing HTML when both `allowHTML` + and `allowMarkdown` are `false`. + +## v0.9.9 (2021-04-26) + +* Updated appservice `StartWebsocket` to return websocket close info. + +## v0.9.8 (2021-04-20) + +* Added methods for getting room tags and account data. + +## v0.9.7 (2021-04-19) + +* **Breaking change (crypto):** `SendEncryptedToDevice` now requires an event + type parameter. Previously it only allowed sending events of type + `event.ToDeviceForwardedRoomKey`. +* Added content structs for VoIP events. +* Added global mutex for Olm decryption + (previously it was only used for encryption). + +## v0.9.6 (2021-04-15) + +* Added option to retry all HTTP requests when encountering a HTTP network + error or gateway error response (502/503/504) + * Disabled by default, you need to set the `DefaultHTTPRetries` field in + the `AppService` or `Client` struct to enable. + * Can also be enabled with `FullRequest`s `MaxAttempts` field. + +## v0.9.5 (2021-04-06) + +* Reverted update of `golang.org/x/sys` which broke Go 1.14 / darwin/arm. + +## v0.9.4 (2021-04-06) + +* Switched appservices to using shared `http.Client` instance with a in-memory + cookie jar. + +## v0.9.3 (2021-03-26) + +* Made user agent headers easier to configure. +* Improved logging when receiving weird/unhandled to-device events. + +## v0.9.2 (2021-03-15) + +* Fixed type of presence state constants (thanks to [@babolivier] in [#30]). +* Implemented presence state fetching methods (thanks to [@babolivier] in [#29]). +* Added support for sending and receiving commands via appservice transaction websocket. + +[@babolivier]: https://github.com/babolivier +[#29]: https://github.com/mautrix/go/pull/29 +[#30]: https://github.com/mautrix/go/pull/30 + +## v0.9.1 (2021-03-11) + +* Fixed appservice register request hiding actual errors due to UIA error handling. + +## v0.9.0 (2021-03-04) + +* **Breaking change (manual API requests):** `MakeFullRequest` now takes a + `FullRequest` struct instead of individual parameters. `MakeRequest`'s + parameters are unchanged. +* **Breaking change (manual /sync):** `SyncRequest` now requires a `Context` + parameter. +* **Breaking change (end-to-bridge encryption):** + the `uk.half-shot.msc2778.login.application_service` constant used for + appservice login ([MSC2778]) was renamed from `AuthTypeAppservice` + to `AuthTypeHalfyAppservice`. + * The `AuthTypeAppservice` constant now contains `m.login.application_service`, + which is currently only used for registrations, but will also be used for + login once MSC2778 lands in the spec. +* Fixed appservice registration requests to include `m.login.application_service` + as the `type` (re [matrix-org/synapse#9548]). +* Added wrapper for `/logout/all`. + +[MSC2778]: https://github.com/matrix-org/matrix-spec-proposals/pull/2778 +[matrix-org/synapse#9548]: https://github.com/matrix-org/synapse/pull/9548 + +## v0.8.6 (2021-03-02) + +* Added client-side timeout to `mautrix.Client`'s `http.Client` + (defaults to 3 minutes). +* Updated maulogger to fix bug where plaintext file logs wouldn't have newlines. + +## v0.8.5 (2021-02-26) + +* Fixed potential concurrent map writes in appservice `Client` and `Intent` + methods. + +## v0.8.4 (2021-02-24) + +* Added option to output appservice logs as JSON. +* Added new methods for validating user ID localparts. + +## v0.8.3 (2021-02-21) + +* Allowed empty content URIs in parser +* Added functions for device management endpoints + (thanks to [@edwargix] in [#26]). + +[@edwargix]: https://github.com/edwargix +[#26]: https://github.com/mautrix/go/pull/26 + +## v0.8.2 (2021-02-09) + +* Fixed error when removing the user's avatar. + +## v0.8.1 (2021-02-09) + +* Added AccountDataStore to remove the need for persistent local storage other + than the access token (thanks to [@daenney] in [#23]). +* Added support for receiving appservice transactions over websocket. + See for the server-side implementation. +* Fixed error when removing the room avatar. + +[@daenney]: https://github.com/daenney +[#23]: https://github.com/mautrix/go/pull/23 + +## v0.8.0 (2020-12-24) + +* **Breaking change:** the `RateLimited` field in the `Registration` struct is + now a pointer, so that it can be omitted entirely. +* Merged initial SSSS/cross-signing code by [@nikofil]. Interactive verification + doesn't work, but the other things mostly do. +* Added support for authorization header auth in appservices ([MSC2832]). +* Added support for receiving ephemeral events directly ([MSC2409]). +* Fixed `SendReaction()` and other similar methods in the `Client` struct. +* Fixed crypto cgo code panicking in Go 1.15.3+. +* Fixed olm session locks sometime getting deadlocked. + +[MSC2832]: https://github.com/matrix-org/matrix-spec-proposals/pull/2832 +[MSC2409]: https://github.com/matrix-org/matrix-spec-proposals/pull/2409 +[@nikofil]: https://github.com/nikofil diff --git a/vendor/maunium.net/go/mautrix/LICENSE b/vendor/maunium.net/go/mautrix/LICENSE new file mode 100644 index 00000000..a612ad98 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/maunium.net/go/mautrix/README.md b/vendor/maunium.net/go/mautrix/README.md new file mode 100644 index 00000000..ac41ca78 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/README.md @@ -0,0 +1,22 @@ +# mautrix-go +[![GoDoc](https://pkg.go.dev/badge/maunium.net/go/mautrix)](https://pkg.go.dev/maunium.net/go/mautrix) + +A Golang Matrix framework. Used by [gomuks](https://matrix.org/docs/projects/client/gomuks), +[go-neb](https://github.com/matrix-org/go-neb), [mautrix-whatsapp](https://github.com/mautrix/whatsapp) +and others. + +Matrix room: [`#go:maunium.net`](https://matrix.to/#/#go:maunium.net) + +This project is based on [matrix-org/gomatrix](https://github.com/matrix-org/gomatrix). +The original project is licensed under [Apache 2.0](https://github.com/matrix-org/gomatrix/blob/master/LICENSE). + +In addition to the basic client API features the original project has, this framework also has: + +* Appservice support (Intent API like mautrix-python, room state storage, etc) +* End-to-end encryption support (incl. interactive SAS verification) +* High-level module for building puppeting bridges +* High-level module for building chat clients +* Wrapper functions for the Synapse admin API +* Structs for parsing event content +* Helpers for parsing and generating Matrix HTML +* Helpers for handling push rules diff --git a/vendor/maunium.net/go/mautrix/client.go b/vendor/maunium.net/go/mautrix/client.go new file mode 100644 index 00000000..95cbacb5 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/client.go @@ -0,0 +1,2708 @@ +// Package mautrix implements the Matrix Client-Server API. +// +// Specification can be found at https://spec.matrix.org/v1.2/client-server-api/ +package mautrix + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "runtime" + "slices" + "strconv" + "strings" + "sync/atomic" + "time" + + "github.com/rs/zerolog" + "go.mau.fi/util/ptr" + "go.mau.fi/util/random" + "go.mau.fi/util/retryafter" + "golang.org/x/exp/maps" + + "maunium.net/go/mautrix/crypto/backup" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" + "maunium.net/go/mautrix/pushrules" +) + +type CryptoHelper interface { + Encrypt(context.Context, id.RoomID, event.Type, any) (*event.EncryptedEventContent, error) + Decrypt(context.Context, *event.Event) (*event.Event, error) + WaitForSession(context.Context, id.RoomID, id.SenderKey, id.SessionID, time.Duration) bool + RequestSession(context.Context, id.RoomID, id.SenderKey, id.SessionID, id.UserID, id.DeviceID) + Init(context.Context) error +} + +type VerificationHelper interface { + // Init initializes the helper. This should be called before any other + // methods. + Init(context.Context) error + + // StartVerification starts an interactive verification flow with the given + // user via a to-device event. + StartVerification(ctx context.Context, to id.UserID) (id.VerificationTransactionID, error) + // StartInRoomVerification starts an interactive verification flow with the + // given user in the given room. + StartInRoomVerification(ctx context.Context, roomID id.RoomID, to id.UserID) (id.VerificationTransactionID, error) + + // AcceptVerification accepts a verification request. + AcceptVerification(ctx context.Context, txnID id.VerificationTransactionID) error + // DismissVerification dismisses a verification request. This will not send + // a cancellation to the other device. This method should only be called + // *before* the request has been accepted and will error otherwise. + DismissVerification(ctx context.Context, txnID id.VerificationTransactionID) error + // CancelVerification cancels a verification request. This method should + // only be called *after* the request has been accepted, although it will + // not error if called beforehand. + CancelVerification(ctx context.Context, txnID id.VerificationTransactionID, code event.VerificationCancelCode, reason string) error + + // HandleScannedQRData handles the data from a QR code scan. + HandleScannedQRData(ctx context.Context, data []byte) error + // ConfirmQRCodeScanned confirms that our QR code has been scanned. + ConfirmQRCodeScanned(ctx context.Context, txnID id.VerificationTransactionID) error + + // StartSAS starts a SAS verification flow. + StartSAS(ctx context.Context, txnID id.VerificationTransactionID) error + // ConfirmSAS indicates that the user has confirmed that the SAS matches + // SAS shown on the other user's device. + ConfirmSAS(ctx context.Context, txnID id.VerificationTransactionID) error +} + +// Client represents a Matrix client. +type Client struct { + HomeserverURL *url.URL // The base homeserver URL + UserID id.UserID // The user ID of the client. Used for forming HTTP paths which use the client's user ID. + DeviceID id.DeviceID // The device ID of the client. + AccessToken string // The access_token for the client. + UserAgent string // The value for the User-Agent header + Client *http.Client // The underlying HTTP client which will be used to make HTTP requests. + Syncer Syncer // The thing which can process /sync responses + Store SyncStore // The thing which can store tokens/ids + StateStore StateStore + Crypto CryptoHelper + Verification VerificationHelper + SpecVersions *RespVersions + ExternalClient *http.Client // The HTTP client used for external (not matrix) media HTTP requests. + + Log zerolog.Logger + + RequestHook func(req *http.Request) + ResponseHook func(req *http.Request, resp *http.Response, err error, duration time.Duration) + + UpdateRequestOnRetry func(req *http.Request, cause error) *http.Request + + SyncPresence event.Presence + SyncTraceLog bool + + StreamSyncMinAge time.Duration + + // Number of times that mautrix will retry any HTTP request + // if the request fails entirely or returns a HTTP gateway error (502-504) + DefaultHTTPRetries int + // Amount of time to wait between HTTP retries, defaults to 4 seconds + DefaultHTTPBackoff time.Duration + // Set to true to disable automatically sleeping on 429 errors. + IgnoreRateLimit bool + + txnID int32 + + // Should the ?user_id= query parameter be set in requests? + // See https://spec.matrix.org/v1.6/application-service-api/#identity-assertion + SetAppServiceUserID bool + // Should the org.matrix.msc3202.device_id query parameter be set in requests? + // See https://github.com/matrix-org/matrix-spec-proposals/pull/3202 + SetAppServiceDeviceID bool + + syncingID uint32 // Identifies the current Sync. Only one Sync can be active at any given time. +} + +type ClientWellKnown struct { + Homeserver HomeserverInfo `json:"m.homeserver"` + IdentityServer IdentityServerInfo `json:"m.identity_server"` +} + +type HomeserverInfo struct { + BaseURL string `json:"base_url"` +} + +type IdentityServerInfo struct { + BaseURL string `json:"base_url"` +} + +// DiscoverClientAPI resolves the client API URL from a Matrix server name. +// Use ParseUserID to extract the server name from a user ID. +// https://spec.matrix.org/v1.2/client-server-api/#server-discovery +func DiscoverClientAPI(ctx context.Context, serverName string) (*ClientWellKnown, error) { + return DiscoverClientAPIWithClient(ctx, &http.Client{Timeout: 30 * time.Second}, serverName) +} + +func DiscoverClientAPIWithClient(ctx context.Context, client *http.Client, serverName string) (*ClientWellKnown, error) { + wellKnownURL := url.URL{ + Scheme: "https", + Host: serverName, + Path: "/.well-known/matrix/client", + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, wellKnownURL.String(), nil) + if err != nil { + return nil, err + } + + if runtime.GOOS != "js" { + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", DefaultUserAgent+" (.well-known fetcher)") + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + return nil, nil + } + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var wellKnown ClientWellKnown + err = json.Unmarshal(data, &wellKnown) + if err != nil { + return nil, errors.New(".well-known response not JSON") + } + + return &wellKnown, nil +} + +// SetCredentials sets the user ID and access token on this client instance. +// +// Deprecated: use the StoreCredentials field in ReqLogin instead. +func (cli *Client) SetCredentials(userID id.UserID, accessToken string) { + cli.AccessToken = accessToken + cli.UserID = userID +} + +// ClearCredentials removes the user ID and access token on this client instance. +func (cli *Client) ClearCredentials() { + cli.AccessToken = "" + cli.UserID = "" + cli.DeviceID = "" +} + +// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the +// error will be nil. +// +// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine. +// Fatal sync errors can be caused by: +// - The failure to create a filter. +// - Client.Syncer.OnFailedSync returning an error in response to a failed sync. +// - Client.Syncer.ProcessResponse returning an error. +// +// If you wish to continue retrying in spite of these fatal errors, call Sync() again. +func (cli *Client) Sync() error { + return cli.SyncWithContext(context.Background()) +} + +func (cli *Client) SyncWithContext(ctx context.Context) error { + // Mark the client as syncing. + // We will keep syncing until the syncing state changes. Either because + // Sync is called or StopSync is called. + syncingID := cli.incrementSyncingID() + nextBatch, err := cli.Store.LoadNextBatch(ctx, cli.UserID) + if err != nil { + return err + } + filterID, err := cli.Store.LoadFilterID(ctx, cli.UserID) + if err != nil { + return err + } + + if filterID == "" { + filterJSON := cli.Syncer.GetFilterJSON(cli.UserID) + resFilter, err := cli.CreateFilter(ctx, filterJSON) + if err != nil { + return err + } + filterID = resFilter.FilterID + if err := cli.Store.SaveFilterID(ctx, cli.UserID, filterID); err != nil { + return err + } + } + lastSuccessfulSync := time.Now().Add(-cli.StreamSyncMinAge - 1*time.Hour) + // Always do first sync with 0 timeout + isFailing := true + for { + streamResp := false + if cli.StreamSyncMinAge > 0 && time.Since(lastSuccessfulSync) > cli.StreamSyncMinAge { + cli.Log.Debug().Msg("Last sync is old, will stream next response") + streamResp = true + } + timeout := 30000 + if isFailing || nextBatch == "" { + timeout = 0 + } + resSync, err := cli.FullSyncRequest(ctx, ReqSync{ + Timeout: timeout, + Since: nextBatch, + FilterID: filterID, + FullState: false, + SetPresence: cli.SyncPresence, + StreamResponse: streamResp, + }) + if err != nil { + isFailing = true + if ctx.Err() != nil { + return ctx.Err() + } + duration, err2 := cli.Syncer.OnFailedSync(resSync, err) + if err2 != nil { + return err2 + } + if duration <= 0 { + continue + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(duration): + continue + } + } + isFailing = false + lastSuccessfulSync = time.Now() + + // Check that the syncing state hasn't changed + // Either because we've stopped syncing or another sync has been started. + // We discard the response from our sync. + if cli.getSyncingID() != syncingID { + return nil + } + + // Save the token now *before* processing it. This means it's possible + // to not process some events, but it means that we won't get constantly stuck processing + // a malformed/buggy event which keeps making us panic. + err = cli.Store.SaveNextBatch(ctx, cli.UserID, resSync.NextBatch) + if err != nil { + return err + } + if err = cli.Syncer.ProcessResponse(ctx, resSync, nextBatch); err != nil { + return err + } + + nextBatch = resSync.NextBatch + } +} + +func (cli *Client) incrementSyncingID() uint32 { + return atomic.AddUint32(&cli.syncingID, 1) +} + +func (cli *Client) getSyncingID() uint32 { + return atomic.LoadUint32(&cli.syncingID) +} + +// StopSync stops the ongoing sync started by Sync. +func (cli *Client) StopSync() { + // Advance the syncing state so that any running Syncs will terminate. + cli.incrementSyncingID() +} + +type contextKey int + +const ( + LogBodyContextKey contextKey = iota + LogRequestIDContextKey + MaxAttemptsContextKey + SyncTokenContextKey +) + +func (cli *Client) RequestStart(req *http.Request) { + if cli != nil && cli.RequestHook != nil { + cli.RequestHook(req) + } +} + +// WithMaxRetries updates the context to set the maximum number of retries for any HTTP requests made with the context. +// +// 0 means the request will only be attempted once and will not be retried. +// Negative values will remove the override and fallback to the defaults. +func WithMaxRetries(ctx context.Context, maxRetries int) context.Context { + return context.WithValue(ctx, MaxAttemptsContextKey, maxRetries+1) +} + +func (cli *Client) LogRequestDone(req *http.Request, resp *http.Response, err error, handlerErr error, contentLength int, duration time.Duration) { + if cli == nil { + return + } + var evt *zerolog.Event + if errors.Is(err, context.Canceled) { + evt = zerolog.Ctx(req.Context()).Warn() + } else if err != nil { + evt = zerolog.Ctx(req.Context()).Err(err) + } else if handlerErr != nil { + evt = zerolog.Ctx(req.Context()).Warn(). + AnErr("body_parse_err", handlerErr) + } else if cli.SyncTraceLog && strings.HasSuffix(req.URL.Path, "/_matrix/client/v3/sync") { + evt = zerolog.Ctx(req.Context()).Trace() + } else { + evt = zerolog.Ctx(req.Context()).Debug() + } + evt = evt. + Str("method", req.Method). + Str("url", req.URL.String()). + Dur("duration", duration) + if cli.ResponseHook != nil { + cli.ResponseHook(req, resp, err, duration) + } + if resp != nil { + mime := resp.Header.Get("Content-Type") + length := resp.ContentLength + if length == -1 && contentLength > 0 { + length = int64(contentLength) + } + evt = evt.Int("status_code", resp.StatusCode). + Int64("response_length", length). + Str("response_mime", mime) + if serverRequestID := resp.Header.Get("X-Beeper-Request-ID"); serverRequestID != "" { + evt.Str("beeper_request_id", serverRequestID) + } + } + if body := req.Context().Value(LogBodyContextKey); body != nil { + evt.Interface("req_body", body) + } + if errors.Is(err, context.Canceled) { + evt.Msg("Request canceled") + } else if err != nil { + evt.Msg("Request failed") + } else if handlerErr != nil { + evt.Msg("Request parsing failed") + } else { + evt.Msg("Request completed") + } +} + +func (cli *Client) MakeRequest(ctx context.Context, method string, httpURL string, reqBody any, resBody any) ([]byte, error) { + return cli.MakeFullRequest(ctx, FullRequest{Method: method, URL: httpURL, RequestJSON: reqBody, ResponseJSON: resBody}) +} + +type ClientResponseHandler = func(req *http.Request, res *http.Response, responseJSON interface{}) ([]byte, error) + +type FullRequest struct { + Method string + URL string + Headers http.Header + RequestJSON interface{} + RequestBytes []byte + RequestBody io.Reader + RequestLength int64 + ResponseJSON interface{} + MaxAttempts int + BackoffDuration time.Duration + SensitiveContent bool + Handler ClientResponseHandler + DontReadResponse bool + Logger *zerolog.Logger + Client *http.Client +} + +var requestID int32 +var logSensitiveContent = os.Getenv("MAUTRIX_LOG_SENSITIVE_CONTENT") == "yes" + +func (params *FullRequest) compileRequest(ctx context.Context) (*http.Request, error) { + reqID := atomic.AddInt32(&requestID, 1) + logger := zerolog.Ctx(ctx) + if logger.GetLevel() == zerolog.Disabled || logger == zerolog.DefaultContextLogger { + logger = params.Logger + } + ctx = logger.With(). + Int32("req_id", reqID). + Logger().WithContext(ctx) + + var logBody any + var reqBody io.Reader + var reqLen int64 + if params.RequestJSON != nil { + jsonStr, err := json.Marshal(params.RequestJSON) + if err != nil { + return nil, HTTPError{ + Message: "failed to marshal JSON", + WrappedError: err, + } + } + if params.SensitiveContent && !logSensitiveContent { + logBody = "" + } else { + logBody = params.RequestJSON + } + reqBody = bytes.NewReader(jsonStr) + reqLen = int64(len(jsonStr)) + } else if params.RequestBytes != nil { + logBody = fmt.Sprintf("<%d bytes>", len(params.RequestBytes)) + reqBody = bytes.NewReader(params.RequestBytes) + reqLen = int64(len(params.RequestBytes)) + } else if params.RequestBody != nil { + logBody = "" + reqLen = -1 + if params.RequestLength > 0 { + logBody = fmt.Sprintf("<%d bytes>", params.RequestLength) + reqLen = params.RequestLength + } else if params.RequestLength == 0 { + zerolog.Ctx(ctx).Warn(). + Msg("RequestBody passed without specifying request length") + } + reqBody = params.RequestBody + if rsc, ok := params.RequestBody.(io.ReadSeekCloser); ok { + // Prevent HTTP from closing the request body, it might be needed for retries + reqBody = nopCloseSeeker{rsc} + } + } else if params.Method != http.MethodGet && params.Method != http.MethodHead { + params.RequestJSON = struct{}{} + logBody = params.RequestJSON + reqBody = bytes.NewReader([]byte("{}")) + reqLen = 2 + } + ctx = context.WithValue(ctx, LogBodyContextKey, logBody) + ctx = context.WithValue(ctx, LogRequestIDContextKey, int(reqID)) + req, err := http.NewRequestWithContext(ctx, params.Method, params.URL, reqBody) + if err != nil { + return nil, HTTPError{ + Message: "failed to create request", + WrappedError: err, + } + } + if params.Headers != nil { + req.Header = params.Headers + } + if params.RequestJSON != nil { + req.Header.Set("Content-Type", "application/json") + } + req.ContentLength = reqLen + return req, nil +} + +func (cli *Client) MakeFullRequest(ctx context.Context, params FullRequest) ([]byte, error) { + data, _, err := cli.MakeFullRequestWithResp(ctx, params) + return data, err +} + +func (cli *Client) MakeFullRequestWithResp(ctx context.Context, params FullRequest) ([]byte, *http.Response, error) { + if cli == nil { + return nil, nil, ErrClientIsNil + } + if cli.HomeserverURL == nil || cli.HomeserverURL.Scheme == "" { + return nil, nil, ErrClientHasNoHomeserver + } + if params.MaxAttempts == 0 { + maxAttempts, ok := ctx.Value(MaxAttemptsContextKey).(int) + if ok && maxAttempts > 0 { + params.MaxAttempts = maxAttempts + } else { + params.MaxAttempts = 1 + cli.DefaultHTTPRetries + } + } + if params.BackoffDuration == 0 { + if cli.DefaultHTTPBackoff == 0 { + params.BackoffDuration = 4 * time.Second + } else { + params.BackoffDuration = cli.DefaultHTTPBackoff + } + } + if params.Logger == nil { + params.Logger = &cli.Log + } + req, err := params.compileRequest(ctx) + if err != nil { + return nil, nil, err + } + if params.Handler == nil { + if params.DontReadResponse { + params.Handler = noopHandleResponse + } else { + params.Handler = handleNormalResponse + } + } + if cli.UserAgent != "" { + req.Header.Set("User-Agent", cli.UserAgent) + } + if len(cli.AccessToken) > 0 { + req.Header.Set("Authorization", "Bearer "+cli.AccessToken) + } + if params.Client == nil { + params.Client = cli.Client + } + return cli.executeCompiledRequest(req, params.MaxAttempts-1, params.BackoffDuration, params.ResponseJSON, params.Handler, params.DontReadResponse, params.Client) +} + +func (cli *Client) cliOrContextLog(ctx context.Context) *zerolog.Logger { + log := zerolog.Ctx(ctx) + if log.GetLevel() == zerolog.Disabled || log == zerolog.DefaultContextLogger { + return &cli.Log + } + return log +} + +func (cli *Client) doRetry(req *http.Request, cause error, retries int, backoff time.Duration, responseJSON any, handler ClientResponseHandler, dontReadResponse bool, client *http.Client) ([]byte, *http.Response, error) { + log := zerolog.Ctx(req.Context()) + if req.Body != nil { + var err error + if req.GetBody != nil { + req.Body, err = req.GetBody() + if err != nil { + log.Warn().Err(err).Msg("Failed to get new body to retry request") + return nil, nil, cause + } + } else if bodySeeker, ok := req.Body.(io.ReadSeeker); ok { + _, err = bodySeeker.Seek(0, io.SeekStart) + if err != nil { + log.Warn().Err(err).Msg("Failed to seek to beginning of request body") + return nil, nil, cause + } + } else { + log.Warn().Msg("Failed to get new body to retry request: GetBody is nil and Body is not an io.ReadSeeker") + return nil, nil, cause + } + } + log.Warn().Err(cause). + Str("method", req.Method). + Str("url", req.URL.String()). + Int("retry_in_seconds", int(backoff.Seconds())). + Msg("Request failed, retrying") + select { + case <-time.After(backoff): + case <-req.Context().Done(): + return nil, nil, req.Context().Err() + } + if cli.UpdateRequestOnRetry != nil { + req = cli.UpdateRequestOnRetry(req, cause) + } + return cli.executeCompiledRequest(req, retries-1, backoff*2, responseJSON, handler, dontReadResponse, client) +} + +func readResponseBody(req *http.Request, res *http.Response) ([]byte, error) { + contents, err := io.ReadAll(res.Body) + if err != nil { + return nil, HTTPError{ + Request: req, + Response: res, + + Message: "failed to read response body", + WrappedError: err, + } + } + return contents, nil +} + +func closeTemp(log *zerolog.Logger, file *os.File) { + _ = file.Close() + err := os.Remove(file.Name()) + if err != nil { + log.Warn().Err(err).Str("file_name", file.Name()).Msg("Failed to remove response temp file") + } +} + +func streamResponse(req *http.Request, res *http.Response, responseJSON interface{}) ([]byte, error) { + log := zerolog.Ctx(req.Context()) + file, err := os.CreateTemp("", "mautrix-response-") + if err != nil { + log.Warn().Err(err).Msg("Failed to create temporary file for streaming response") + _, err = handleNormalResponse(req, res, responseJSON) + return nil, err + } + defer closeTemp(log, file) + if _, err = io.Copy(file, res.Body); err != nil { + return nil, fmt.Errorf("failed to copy response to file: %w", err) + } else if _, err = file.Seek(0, 0); err != nil { + return nil, fmt.Errorf("failed to seek to beginning of response file: %w", err) + } else if err = json.NewDecoder(file).Decode(responseJSON); err != nil { + return nil, fmt.Errorf("failed to unmarshal response body: %w", err) + } else { + return nil, nil + } +} + +func noopHandleResponse(req *http.Request, res *http.Response, responseJSON interface{}) ([]byte, error) { + return nil, nil +} + +func handleNormalResponse(req *http.Request, res *http.Response, responseJSON interface{}) ([]byte, error) { + if contents, err := readResponseBody(req, res); err != nil { + return nil, err + } else if responseJSON == nil { + return contents, nil + } else if err = json.Unmarshal(contents, &responseJSON); err != nil { + return nil, HTTPError{ + Request: req, + Response: res, + + Message: "failed to unmarshal response body", + ResponseBody: string(contents), + WrappedError: err, + } + } else { + return contents, nil + } +} + +func ParseErrorResponse(req *http.Request, res *http.Response) ([]byte, error) { + contents, err := readResponseBody(req, res) + if err != nil { + return contents, err + } + + respErr := &RespError{ + StatusCode: res.StatusCode, + } + if _ = json.Unmarshal(contents, respErr); respErr.ErrCode == "" { + respErr = nil + } + + return contents, HTTPError{ + Request: req, + Response: res, + RespError: respErr, + } +} + +func (cli *Client) executeCompiledRequest(req *http.Request, retries int, backoff time.Duration, responseJSON any, handler ClientResponseHandler, dontReadResponse bool, client *http.Client) ([]byte, *http.Response, error) { + cli.RequestStart(req) + startTime := time.Now() + res, err := client.Do(req) + duration := time.Now().Sub(startTime) + if res != nil && !dontReadResponse { + defer res.Body.Close() + } + if err != nil { + if retries > 0 && !errors.Is(err, context.Canceled) { + return cli.doRetry(req, err, retries, backoff, responseJSON, handler, dontReadResponse, client) + } + err = HTTPError{ + Request: req, + Response: res, + + Message: "request error", + WrappedError: err, + } + cli.LogRequestDone(req, res, err, nil, 0, duration) + return nil, res, err + } + + if retries > 0 && retryafter.Should(res.StatusCode, !cli.IgnoreRateLimit) { + backoff = retryafter.Parse(res.Header.Get("Retry-After"), backoff) + return cli.doRetry(req, fmt.Errorf("HTTP %d", res.StatusCode), retries, backoff, responseJSON, handler, dontReadResponse, client) + } + + var body []byte + if res.StatusCode < 200 || res.StatusCode >= 300 { + body, err = ParseErrorResponse(req, res) + cli.LogRequestDone(req, res, nil, nil, len(body), duration) + } else { + body, err = handler(req, res, responseJSON) + cli.LogRequestDone(req, res, nil, err, len(body), duration) + } + return body, res, err +} + +// Whoami gets the user ID of the current user. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3accountwhoami +func (cli *Client) Whoami(ctx context.Context) (resp *RespWhoami, err error) { + urlPath := cli.BuildClientURL("v3", "account", "whoami") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// CreateFilter makes an HTTP request according to https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridfilter +func (cli *Client) CreateFilter(ctx context.Context, filter *Filter) (resp *RespCreateFilter, err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "filter") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, filter, &resp) + return +} + +// SyncRequest makes an HTTP request according to https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3sync +func (cli *Client) SyncRequest(ctx context.Context, timeout int, since, filterID string, fullState bool, setPresence event.Presence) (resp *RespSync, err error) { + return cli.FullSyncRequest(ctx, ReqSync{ + Timeout: timeout, + Since: since, + FilterID: filterID, + FullState: fullState, + SetPresence: setPresence, + }) +} + +type ReqSync struct { + Timeout int + Since string + FilterID string + FullState bool + SetPresence event.Presence + StreamResponse bool + UseStateAfter bool + BeeperStreaming bool + Client *http.Client +} + +func (req *ReqSync) BuildQuery() map[string]string { + query := map[string]string{ + "timeout": strconv.Itoa(req.Timeout), + } + if req.Since != "" { + query["since"] = req.Since + } + if req.FilterID != "" { + query["filter"] = req.FilterID + } + if req.SetPresence != "" { + query["set_presence"] = string(req.SetPresence) + } + if req.FullState { + query["full_state"] = "true" + } + if req.UseStateAfter { + query["use_state_after"] = "true" + } + if req.BeeperStreaming { + query["com.beeper.streaming"] = "true" + } + return query +} + +// FullSyncRequest makes an HTTP request according to https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3sync +func (cli *Client) FullSyncRequest(ctx context.Context, req ReqSync) (resp *RespSync, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "sync"}, req.BuildQuery()) + fullReq := FullRequest{ + Method: http.MethodGet, + URL: urlPath, + ResponseJSON: &resp, + Client: req.Client, + // We don't want automatic retries for SyncRequest, the Sync() wrapper handles those. + MaxAttempts: 1, + } + if req.StreamResponse { + fullReq.Handler = streamResponse + } + start := time.Now() + _, err = cli.MakeFullRequest(ctx, fullReq) + duration := time.Now().Sub(start) + timeout := time.Duration(req.Timeout) * time.Millisecond + buffer := 10 * time.Second + if req.Since == "" { + buffer = 1 * time.Minute + } + if err == nil && duration > timeout+buffer { + cli.cliOrContextLog(ctx).Warn(). + Str("since", req.Since). + Dur("duration", duration). + Dur("timeout", timeout). + Msg("Sync request took unusually long") + } + return +} + +// RegisterAvailable checks if a username is valid and available for registration on the server. +// +// See https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3registeravailable for more details +// +// This will always return an error if the username isn't available, so checking the actual response struct is generally +// not necessary. It is still returned for future-proofing. For a simple availability check, just check that the returned +// error is nil. `errors.Is` can be used to find the exact reason why a username isn't available: +// +// _, err := cli.RegisterAvailable("cat") +// if errors.Is(err, mautrix.MUserInUse) { +// // Username is taken +// } else if errors.Is(err, mautrix.MInvalidUsername) { +// // Username is not valid +// } else if errors.Is(err, mautrix.MExclusive) { +// // Username is reserved for an appservice +// } else if errors.Is(err, mautrix.MLimitExceeded) { +// // Too many requests +// } else if err != nil { +// // Unknown error +// } else { +// // Username is available +// } +func (cli *Client) RegisterAvailable(ctx context.Context, username string) (resp *RespRegisterAvailable, err error) { + u := cli.BuildURLWithQuery(ClientURLPath{"v3", "register", "available"}, map[string]string{"username": username}) + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, &resp) + if err == nil && !resp.Available { + err = fmt.Errorf(`request returned OK status without "available": true`) + } + return +} + +func (cli *Client) register(ctx context.Context, url string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) { + var bodyBytes []byte + bodyBytes, err = cli.MakeFullRequest(ctx, FullRequest{ + Method: http.MethodPost, + URL: url, + RequestJSON: req, + SensitiveContent: len(req.Password) > 0, + }) + if err != nil { + httpErr, ok := err.(HTTPError) + // if response has a 401 status, but doesn't have the errcode field, it's probably a UIA response. + if ok && httpErr.IsStatus(http.StatusUnauthorized) && httpErr.RespError == nil { + err = json.Unmarshal(bodyBytes, &uiaResp) + } + } else { + // body should be RespRegister + err = json.Unmarshal(bodyBytes, &resp) + } + return +} + +// Register makes an HTTP request according to https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register +// +// Registers with kind=user. For kind=guest, see RegisterGuest. +func (cli *Client) Register(ctx context.Context, req *ReqRegister) (*RespRegister, *RespUserInteractive, error) { + u := cli.BuildClientURL("v3", "register") + return cli.register(ctx, u, req) +} + +// RegisterGuest makes an HTTP request according to https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register +// with kind=guest. +// +// For kind=user, see Register. +func (cli *Client) RegisterGuest(ctx context.Context, req *ReqRegister) (*RespRegister, *RespUserInteractive, error) { + query := map[string]string{ + "kind": "guest", + } + u := cli.BuildURLWithQuery(ClientURLPath{"v3", "register"}, query) + return cli.register(ctx, u, req) +} + +// RegisterDummy performs m.login.dummy registration according to https://spec.matrix.org/v1.2/client-server-api/#dummy-auth +// +// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration +// this way. If the homeserver does not, an error is returned. +// +// This does not set credentials on the client instance. See SetCredentials() instead. +// +// res, err := cli.RegisterDummy(&mautrix.ReqRegister{ +// Username: "alice", +// Password: "wonderland", +// }) +// if err != nil { +// panic(err) +// } +// token := res.AccessToken +func (cli *Client) RegisterDummy(ctx context.Context, req *ReqRegister) (*RespRegister, error) { + res, uia, err := cli.Register(ctx, req) + if err != nil && uia == nil { + return nil, err + } else if uia == nil { + return nil, errors.New("server did not return user-interactive auth flows") + } else if !uia.HasSingleStageFlow(AuthTypeDummy) { + return nil, errors.New("server does not support m.login.dummy") + } + req.Auth = BaseAuthData{Type: AuthTypeDummy, Session: uia.Session} + res, _, err = cli.Register(ctx, req) + if err != nil { + return nil, err + } + return res, nil +} + +// GetLoginFlows fetches the login flows that the homeserver supports using https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3login +func (cli *Client) GetLoginFlows(ctx context.Context) (resp *RespLoginFlows, err error) { + urlPath := cli.BuildClientURL("v3", "login") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// Login a user to the homeserver according to https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login +func (cli *Client) Login(ctx context.Context, req *ReqLogin) (resp *RespLogin, err error) { + _, err = cli.MakeFullRequest(ctx, FullRequest{ + Method: http.MethodPost, + URL: cli.BuildClientURL("v3", "login"), + RequestJSON: req, + ResponseJSON: &resp, + SensitiveContent: len(req.Password) > 0 || len(req.Token) > 0, + }) + if req.StoreCredentials && err == nil { + cli.DeviceID = resp.DeviceID + cli.AccessToken = resp.AccessToken + cli.UserID = resp.UserID + + cli.Log.Debug(). + Str("user_id", cli.UserID.String()). + Str("device_id", cli.DeviceID.String()). + Msg("Stored credentials after login") + } + if req.StoreHomeserverURL && err == nil && resp.WellKnown != nil && len(resp.WellKnown.Homeserver.BaseURL) > 0 { + var urlErr error + cli.HomeserverURL, urlErr = url.Parse(resp.WellKnown.Homeserver.BaseURL) + if urlErr != nil { + cli.Log.Warn(). + Err(urlErr). + Str("homeserver_url", resp.WellKnown.Homeserver.BaseURL). + Msg("Failed to parse homeserver URL in login response") + } else { + cli.Log.Debug(). + Str("homeserver_url", cli.HomeserverURL.String()). + Msg("Updated homeserver URL after login") + } + } + return +} + +// Create a device for an appservice user using MSC4190. +func (cli *Client) CreateDeviceMSC4190(ctx context.Context, deviceID id.DeviceID, initialDisplayName string) error { + if len(deviceID) == 0 { + deviceID = id.DeviceID(strings.ToUpper(random.String(10))) + } + _, err := cli.MakeRequest(ctx, http.MethodPut, cli.BuildClientURL("v3", "devices", deviceID), &ReqPutDevice{ + DisplayName: initialDisplayName, + }, nil) + if err != nil { + return err + } + cli.DeviceID = deviceID + cli.SetAppServiceDeviceID = true + return nil +} + +// Logout the current user. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout +// This does not clear the credentials from the client instance. See ClearCredentials() instead. +func (cli *Client) Logout(ctx context.Context) (resp *RespLogout, err error) { + urlPath := cli.BuildClientURL("v3", "logout") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, nil, &resp) + return +} + +// LogoutAll logs out all the devices of the current user. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logoutall +// This does not clear the credentials from the client instance. See ClearCredentials() instead. +func (cli *Client) LogoutAll(ctx context.Context) (resp *RespLogout, err error) { + urlPath := cli.BuildClientURL("v3", "logout", "all") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, nil, &resp) + return +} + +// Versions returns the list of supported Matrix versions on this homeserver. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientversions +func (cli *Client) Versions(ctx context.Context) (resp *RespVersions, err error) { + urlPath := cli.BuildClientURL("versions") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + if resp != nil { + cli.SpecVersions = resp + } + return +} + +// Capabilities returns capabilities on this homeserver. See https://spec.matrix.org/v1.3/client-server-api/#capabilities-negotiation +func (cli *Client) Capabilities(ctx context.Context) (resp *RespCapabilities, err error) { + urlPath := cli.BuildClientURL("v3", "capabilities") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// JoinRoom joins the client to a room ID or alias. See https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv3joinroomidoralias +// +// The last parameter contains optional extra fields and can be left nil. +func (cli *Client) JoinRoom(ctx context.Context, roomIDorAlias string, req *ReqJoinRoom) (resp *RespJoinRoom, err error) { + if req == nil { + req = &ReqJoinRoom{} + } + urlPath := cli.BuildURLWithFullQuery(ClientURLPath{"v3", "join", roomIDorAlias}, func(q url.Values) { + if len(req.Via) > 0 { + q["via"] = req.Via + } + }) + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, resp.RoomID, cli.UserID, event.MembershipJoin) + if err != nil { + err = fmt.Errorf("failed to update state store: %w", err) + } + } + return +} + +// KnockRoom requests to join a room ID or alias. See https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv3knockroomidoralias +// +// The last parameter contains optional extra fields and can be left nil. +func (cli *Client) KnockRoom(ctx context.Context, roomIDorAlias string, req *ReqKnockRoom) (resp *RespKnockRoom, err error) { + if req == nil { + req = &ReqKnockRoom{} + } + urlPath := cli.BuildURLWithFullQuery(ClientURLPath{"v3", "knock", roomIDorAlias}, func(q url.Values) { + if len(req.Via) > 0 { + q["via"] = req.Via + } + }) + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, resp.RoomID, cli.UserID, event.MembershipKnock) + if err != nil { + err = fmt.Errorf("failed to update state store: %w", err) + } + } + return +} + +// JoinRoomByID joins the client to a room ID. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidjoin +// +// Unlike JoinRoom, this method can only be used to join rooms that the server already knows about. +// It's mostly intended for bridges and other things where it's already certain that the server is in the room. +func (cli *Client) JoinRoomByID(ctx context.Context, roomID id.RoomID) (resp *RespJoinRoom, err error) { + _, err = cli.MakeRequest(ctx, http.MethodPost, cli.BuildClientURL("v3", "rooms", roomID, "join"), nil, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, resp.RoomID, cli.UserID, event.MembershipJoin) + if err != nil { + err = fmt.Errorf("failed to update state store: %w", err) + } + } + return +} + +func (cli *Client) GetProfile(ctx context.Context, mxid id.UserID) (resp *RespUserProfile, err error) { + urlPath := cli.BuildClientURL("v3", "profile", mxid) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) SearchUserDirectory(ctx context.Context, query string, limit int) (resp *RespSearchUserDirectory, err error) { + urlPath := cli.BuildClientURL("v3", "user_directory", "search") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, &ReqSearchUserDirectory{ + SearchTerm: query, + Limit: limit, + }, &resp) + return +} + +func (cli *Client) GetMutualRooms(ctx context.Context, otherUserID id.UserID, extras ...ReqMutualRooms) (resp *RespMutualRooms, err error) { + if cli.SpecVersions != nil && !cli.SpecVersions.Supports(FeatureMutualRooms) { + err = fmt.Errorf("server does not support fetching mutual rooms") + return + } + query := map[string]string{ + "user_id": otherUserID.String(), + } + if len(extras) > 0 { + query["from"] = extras[0].From + } + urlPath := cli.BuildURLWithQuery(ClientURLPath{"unstable", "uk.half-shot.msc2666", "user", "mutual_rooms"}, query) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) GetRoomSummary(ctx context.Context, roomIDOrAlias string, via ...string) (resp *RespRoomSummary, err error) { + urlPath := ClientURLPath{"unstable", "im.nheko.summary", "summary", roomIDOrAlias} + if cli.SpecVersions.ContainsGreaterOrEqual(SpecV115) { + urlPath = ClientURLPath{"v1", "room_summary", roomIDOrAlias} + } + // TODO add version check after one is added to MSC3266 + fullURL := cli.BuildURLWithFullQuery(urlPath, func(q url.Values) { + if len(via) > 0 { + q["via"] = via + } + }) + _, err = cli.MakeRequest(ctx, http.MethodGet, fullURL, nil, &resp) + return +} + +// GetDisplayName returns the display name of the user with the specified MXID. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseriddisplayname +func (cli *Client) GetDisplayName(ctx context.Context, mxid id.UserID) (resp *RespUserDisplayName, err error) { + err = cli.GetProfileField(ctx, mxid, "displayname", &resp) + return +} + +// GetOwnDisplayName returns the user's display name. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseriddisplayname +func (cli *Client) GetOwnDisplayName(ctx context.Context) (resp *RespUserDisplayName, err error) { + return cli.GetDisplayName(ctx, cli.UserID) +} + +// SetDisplayName sets the user's profile display name. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3profileuseriddisplayname +func (cli *Client) SetDisplayName(ctx context.Context, displayName string) (err error) { + return cli.SetProfileField(ctx, "displayname", displayName) +} + +// SetProfileField sets an arbitrary profile field. See https://spec.matrix.org/v1.16/client-server-api/#put_matrixclientv3profileuseridkeyname +func (cli *Client) SetProfileField(ctx context.Context, key string, value any) (err error) { + urlPath := cli.BuildClientURL("v3", "profile", cli.UserID, key) + if key != "displayname" && key != "avatar_url" && !cli.SpecVersions.Supports(FeatureArbitraryProfileFields) && cli.SpecVersions.Supports(FeatureUnstableProfileFields) { + urlPath = cli.BuildClientURL("unstable", "uk.tcpip.msc4133", "profile", cli.UserID, key) + } + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, map[string]any{ + key: value, + }, nil) + return +} + +// DeleteProfileField deletes an arbitrary profile field. See https://spec.matrix.org/v1.16/client-server-api/#put_matrixclientv3profileuseridkeyname +func (cli *Client) DeleteProfileField(ctx context.Context, key string) (err error) { + urlPath := cli.BuildClientURL("v3", "profile", cli.UserID, key) + if key != "displayname" && key != "avatar_url" && !cli.SpecVersions.Supports(FeatureArbitraryProfileFields) && cli.SpecVersions.Supports(FeatureUnstableProfileFields) { + urlPath = cli.BuildClientURL("unstable", "uk.tcpip.msc4133", "profile", cli.UserID, key) + } + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, nil) + return +} + +// GetProfileField gets an arbitrary profile field and parses the response into the given struct. See https://spec.matrix.org/unstable/client-server-api/#get_matrixclientv3profileuseridkeyname +func (cli *Client) GetProfileField(ctx context.Context, userID id.UserID, key string, into any) (err error) { + urlPath := cli.BuildClientURL("v3", "profile", userID, key) + if key != "displayname" && key != "avatar_url" && !cli.SpecVersions.Supports(FeatureArbitraryProfileFields) && cli.SpecVersions.Supports(FeatureUnstableProfileFields) { + urlPath = cli.BuildClientURL("unstable", "uk.tcpip.msc4133", "profile", cli.UserID, key) + } + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, into) + return +} + +// GetAvatarURL gets the avatar URL of the user with the specified MXID. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseridavatar_url +func (cli *Client) GetAvatarURL(ctx context.Context, mxid id.UserID) (url id.ContentURI, err error) { + s := struct { + AvatarURL id.ContentURI `json:"avatar_url"` + }{} + err = cli.GetProfileField(ctx, mxid, "avatar_url", &s) + url = s.AvatarURL + return +} + +// GetOwnAvatarURL gets the user's avatar URL. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseridavatar_url +func (cli *Client) GetOwnAvatarURL(ctx context.Context) (url id.ContentURI, err error) { + return cli.GetAvatarURL(ctx, cli.UserID) +} + +// SetAvatarURL sets the user's avatar URL. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3profileuseridavatar_url +func (cli *Client) SetAvatarURL(ctx context.Context, url id.ContentURI) (err error) { + urlPath := cli.BuildClientURL("v3", "profile", cli.UserID, "avatar_url") + s := struct { + AvatarURL string `json:"avatar_url"` + }{url.String()} + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, &s, nil) + if err != nil { + return err + } + + return nil +} + +// BeeperUpdateProfile sets custom fields in the user's profile. +func (cli *Client) BeeperUpdateProfile(ctx context.Context, data any) (err error) { + urlPath := cli.BuildClientURL("v3", "profile", cli.UserID) + _, err = cli.MakeRequest(ctx, http.MethodPatch, urlPath, data, nil) + return +} + +// GetAccountData gets the user's account data of this type. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3useruseridaccount_datatype +func (cli *Client) GetAccountData(ctx context.Context, name string, output interface{}) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "account_data", name) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, output) + return +} + +// SetAccountData sets the user's account data of this type. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridaccount_datatype +func (cli *Client) SetAccountData(ctx context.Context, name string, data interface{}) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "account_data", name) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, data, nil) + if err != nil { + return err + } + + return nil +} + +// GetRoomAccountData gets the user's account data of this type in a specific room. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridaccount_datatype +func (cli *Client) GetRoomAccountData(ctx context.Context, roomID id.RoomID, name string, output interface{}) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "rooms", roomID, "account_data", name) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, output) + return +} + +// SetRoomAccountData sets the user's account data of this type in a specific room. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridroomsroomidaccount_datatype +func (cli *Client) SetRoomAccountData(ctx context.Context, roomID id.RoomID, name string, data interface{}) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "rooms", roomID, "account_data", name) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, data, nil) + if err != nil { + return err + } + + return nil +} + +// SendMessageEvent sends a message event into a room. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendMessageEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...ReqSendEvent) (resp *RespSendEvent, err error) { + var req ReqSendEvent + if len(extra) > 0 { + req = extra[0] + } + + var txnID string + if len(req.TransactionID) > 0 { + txnID = req.TransactionID + } else { + txnID = cli.TxnID() + } + + queryParams := map[string]string{} + if req.Timestamp > 0 { + queryParams["ts"] = strconv.FormatInt(req.Timestamp, 10) + } + if req.MeowEventID != "" { + queryParams["fi.mau.event_id"] = req.MeowEventID.String() + } + if req.UnstableDelay > 0 { + queryParams["org.matrix.msc4140.delay"] = strconv.FormatInt(req.UnstableDelay.Milliseconds(), 10) + } + + if !req.DontEncrypt && cli != nil && cli.Crypto != nil && eventType != event.EventReaction && eventType != event.EventEncrypted { + var isEncrypted bool + isEncrypted, err = cli.StateStore.IsEncrypted(ctx, roomID) + if err != nil { + err = fmt.Errorf("failed to check if room is encrypted: %w", err) + return + } + if isEncrypted { + if contentJSON, err = cli.Crypto.Encrypt(ctx, roomID, eventType, contentJSON); err != nil { + err = fmt.Errorf("failed to encrypt event: %w", err) + return + } + eventType = event.EventEncrypted + } + } + + urlData := ClientURLPath{"v3", "rooms", roomID, "send", eventType.String(), txnID} + urlPath := cli.BuildURLWithQuery(urlData, queryParams) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, contentJSON, &resp) + return +} + +// SendStateEvent sends a state event into a room. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendStateEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, stateKey string, contentJSON interface{}, extra ...ReqSendEvent) (resp *RespSendEvent, err error) { + var req ReqSendEvent + if len(extra) > 0 { + req = extra[0] + } + + queryParams := map[string]string{} + if req.MeowEventID != "" { + queryParams["fi.mau.event_id"] = req.MeowEventID.String() + } + if req.UnstableDelay > 0 { + queryParams["org.matrix.msc4140.delay"] = strconv.FormatInt(req.UnstableDelay.Milliseconds(), 10) + } + + urlData := ClientURLPath{"v3", "rooms", roomID, "state", eventType.String(), stateKey} + urlPath := cli.BuildURLWithQuery(urlData, queryParams) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, contentJSON, &resp) + if err == nil && cli.StateStore != nil && req.UnstableDelay == 0 { + cli.updateStoreWithOutgoingEvent(ctx, roomID, eventType, stateKey, contentJSON) + } + return +} + +// SendMassagedStateEvent sends a state event into a room with a custom timestamp. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendMassagedStateEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, stateKey string, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "rooms", roomID, "state", eventType.String(), stateKey}, map[string]string{ + "ts": strconv.FormatInt(ts, 10), + }) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, contentJSON, &resp) + if err == nil && cli.StateStore != nil { + cli.updateStoreWithOutgoingEvent(ctx, roomID, eventType, stateKey, contentJSON) + } + return +} + +func (cli *Client) DelayedEvents(ctx context.Context, req *ReqDelayedEvents) (resp *RespDelayedEvents, err error) { + query := map[string]string{} + if req.DelayID != "" { + query["delay_id"] = string(req.DelayID) + } + if req.Status != "" { + query["status"] = string(req.Status) + } + if req.NextBatch != "" { + query["next_batch"] = req.NextBatch + } + + urlPath := cli.BuildURLWithQuery(ClientURLPath{"unstable", "org.matrix.msc4140", "delayed_events"}, query) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, req, &resp) + + // Migration: merge old keys with new ones + if resp != nil { + resp.Scheduled = append(resp.Scheduled, resp.DelayedEvents...) + resp.DelayedEvents = nil + resp.Finalised = append(resp.Finalised, resp.FinalisedEvents...) + resp.FinalisedEvents = nil + } + + return +} + +func (cli *Client) UpdateDelayedEvent(ctx context.Context, req *ReqUpdateDelayedEvent) (resp *RespUpdateDelayedEvent, err error) { + urlPath := cli.BuildClientURL("unstable", "org.matrix.msc4140", "delayed_events", req.DelayID) + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +// SendText sends an m.room.message event into the given room with a msgtype of m.text +// See https://spec.matrix.org/v1.2/client-server-api/#mtext +func (cli *Client) SendText(ctx context.Context, roomID id.RoomID, text string) (*RespSendEvent, error) { + return cli.SendMessageEvent(ctx, roomID, event.EventMessage, &event.MessageEventContent{ + MsgType: event.MsgText, + Body: text, + }) +} + +// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice +// See https://spec.matrix.org/v1.2/client-server-api/#mnotice +func (cli *Client) SendNotice(ctx context.Context, roomID id.RoomID, text string) (*RespSendEvent, error) { + return cli.SendMessageEvent(ctx, roomID, event.EventMessage, &event.MessageEventContent{ + MsgType: event.MsgNotice, + Body: text, + }) +} + +func (cli *Client) SendReaction(ctx context.Context, roomID id.RoomID, eventID id.EventID, reaction string) (*RespSendEvent, error) { + return cli.SendMessageEvent(ctx, roomID, event.EventReaction, &event.ReactionEventContent{ + RelatesTo: event.RelatesTo{ + EventID: eventID, + Type: event.RelAnnotation, + Key: reaction, + }, + }) +} + +// RedactEvent redacts the given event. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidredacteventidtxnid +func (cli *Client) RedactEvent(ctx context.Context, roomID id.RoomID, eventID id.EventID, extra ...ReqRedact) (resp *RespSendEvent, err error) { + req := ReqRedact{} + if len(extra) > 0 { + req = extra[0] + } + if req.Extra == nil { + req.Extra = make(map[string]interface{}) + } + if len(req.Reason) > 0 { + req.Extra["reason"] = req.Reason + } + var txnID string + if len(req.TxnID) > 0 { + txnID = req.TxnID + } else { + txnID = cli.TxnID() + } + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "redact", eventID, txnID) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, req.Extra, &resp) + return +} + +func (cli *Client) UnstableRedactUserEvents(ctx context.Context, roomID id.RoomID, userID id.UserID, req *ReqRedactUser) (resp *RespRedactUserEvents, err error) { + if req == nil { + req = &ReqRedactUser{} + } + query := map[string]string{} + if req.Limit > 0 { + query["limit"] = strconv.Itoa(req.Limit) + } + urlPath := cli.BuildURLWithQuery(ClientURLPath{"unstable", "org.matrix.msc4194", "rooms", roomID, "redact", "user", userID}, query) + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +// CreateRoom creates a new Matrix room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom +// +// resp, err := cli.CreateRoom(&mautrix.ReqCreateRoom{ +// Preset: "public_chat", +// }) +// fmt.Println("Room:", resp.RoomID) +func (cli *Client) CreateRoom(ctx context.Context, req *ReqCreateRoom) (resp *RespCreateRoom, err error) { + urlPath := cli.BuildClientURL("v3", "createRoom") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + if err == nil && cli.StateStore != nil { + storeErr := cli.StateStore.SetMembership(ctx, resp.RoomID, cli.UserID, event.MembershipJoin) + if storeErr != nil { + cli.cliOrContextLog(ctx).Warn().Err(storeErr). + Stringer("creator_user_id", cli.UserID). + Msg("Failed to update creator membership in state store after creating room") + } + for _, evt := range req.InitialState { + evt.RoomID = resp.RoomID + if evt.StateKey == nil { + evt.StateKey = ptr.Ptr("") + } + UpdateStateStore(ctx, cli.StateStore, evt) + } + inviteMembership := event.MembershipInvite + if req.BeeperAutoJoinInvites { + inviteMembership = event.MembershipJoin + } + for _, invitee := range req.Invite { + storeErr = cli.StateStore.SetMembership(ctx, resp.RoomID, invitee, inviteMembership) + if storeErr != nil { + cli.cliOrContextLog(ctx).Warn().Err(storeErr). + Stringer("invitee_user_id", invitee). + Msg("Failed to update membership in state store after creating room") + } + } + } + return +} + +// LeaveRoom leaves the given room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidleave +func (cli *Client) LeaveRoom(ctx context.Context, roomID id.RoomID, optionalReq ...*ReqLeave) (resp *RespLeaveRoom, err error) { + req := &ReqLeave{} + if len(optionalReq) == 1 { + req = optionalReq[0] + } else if len(optionalReq) > 1 { + panic("invalid number of arguments to LeaveRoom") + } + u := cli.BuildClientURL("v3", "rooms", roomID, "leave") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, roomID, cli.UserID, event.MembershipLeave) + if err != nil { + err = fmt.Errorf("failed to update membership in state store: %w", err) + } + } + return +} + +// ForgetRoom forgets a room entirely. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidforget +func (cli *Client) ForgetRoom(ctx context.Context, roomID id.RoomID) (resp *RespForgetRoom, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "forget") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, struct{}{}, &resp) + return +} + +// InviteUser invites a user to a room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite +func (cli *Client) InviteUser(ctx context.Context, roomID id.RoomID, req *ReqInviteUser) (resp *RespInviteUser, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "invite") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, roomID, req.UserID, event.MembershipInvite) + if err != nil { + err = fmt.Errorf("failed to update membership in state store: %w", err) + } + } + return +} + +// InviteUserByThirdParty invites a third-party identifier to a room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite-1 +func (cli *Client) InviteUserByThirdParty(ctx context.Context, roomID id.RoomID, req *ReqInvite3PID) (resp *RespInviteUser, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "invite") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + return +} + +// KickUser kicks a user from a room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick +func (cli *Client) KickUser(ctx context.Context, roomID id.RoomID, req *ReqKickUser) (resp *RespKickUser, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "kick") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, roomID, req.UserID, event.MembershipLeave) + if err != nil { + err = fmt.Errorf("failed to update membership in state store: %w", err) + } + } + return +} + +// BanUser bans a user from a room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban +func (cli *Client) BanUser(ctx context.Context, roomID id.RoomID, req *ReqBanUser) (resp *RespBanUser, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "ban") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, roomID, req.UserID, event.MembershipBan) + if err != nil { + err = fmt.Errorf("failed to update membership in state store: %w", err) + } + } + return +} + +// UnbanUser unbans a user from a room. See https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban +func (cli *Client) UnbanUser(ctx context.Context, roomID id.RoomID, req *ReqUnbanUser) (resp *RespUnbanUser, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "unban") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + if err == nil && cli.StateStore != nil { + err = cli.StateStore.SetMembership(ctx, roomID, req.UserID, event.MembershipLeave) + if err != nil { + err = fmt.Errorf("failed to update membership in state store: %w", err) + } + } + return +} + +// UserTyping sets the typing status of the user. See https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid +func (cli *Client) UserTyping(ctx context.Context, roomID id.RoomID, typing bool, timeout time.Duration) (resp *RespTyping, err error) { + req := ReqTyping{Typing: typing, Timeout: timeout.Milliseconds()} + u := cli.BuildClientURL("v3", "rooms", roomID, "typing", cli.UserID) + _, err = cli.MakeRequest(ctx, http.MethodPut, u, req, &resp) + return +} + +// GetPresence gets the presence of the user with the specified MXID. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3presenceuseridstatus +func (cli *Client) GetPresence(ctx context.Context, userID id.UserID) (resp *RespPresence, err error) { + resp = new(RespPresence) + u := cli.BuildClientURL("v3", "presence", userID, "status") + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, resp) + return +} + +// GetOwnPresence gets the user's presence. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3presenceuseridstatus +func (cli *Client) GetOwnPresence(ctx context.Context) (resp *RespPresence, err error) { + return cli.GetPresence(ctx, cli.UserID) +} + +func (cli *Client) SetPresence(ctx context.Context, presence ReqPresence) (err error) { + u := cli.BuildClientURL("v3", "presence", cli.UserID, "status") + _, err = cli.MakeRequest(ctx, http.MethodPut, u, presence, nil) + return +} + +func (cli *Client) updateStoreWithOutgoingEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, stateKey string, contentJSON interface{}) { + if cli == nil || cli.StateStore == nil { + return + } + fakeEvt := &event.Event{ + StateKey: &stateKey, + Type: eventType, + RoomID: roomID, + } + var err error + fakeEvt.Content.VeryRaw, err = json.Marshal(contentJSON) + if err != nil { + cli.Log.Warn().Err(err).Msg("Failed to marshal state event content to update state store") + return + } + err = json.Unmarshal(fakeEvt.Content.VeryRaw, &fakeEvt.Content.Raw) + if err != nil { + cli.Log.Warn().Err(err).Msg("Failed to unmarshal state event content to update state store") + return + } + err = fakeEvt.Content.ParseRaw(fakeEvt.Type) + if err != nil { + switch fakeEvt.Type { + case event.StateMember, event.StatePowerLevels, event.StateEncryption: + cli.Log.Warn().Err(err).Msg("Failed to parse state event content to update state store") + default: + cli.Log.Debug().Err(err).Msg("Failed to parse state event content to update state store") + } + return + } + UpdateStateStore(ctx, cli.StateStore, fakeEvt) +} + +// StateEvent gets the content of a single state event in a room. +// It will attempt to JSON unmarshal into the given "outContent" struct with the HTTP response body, or return an error. +// See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidstateeventtypestatekey +func (cli *Client) StateEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, stateKey string, outContent interface{}) (err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "state", eventType.String(), stateKey) + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, outContent) + if err == nil && cli.StateStore != nil { + cli.updateStoreWithOutgoingEvent(ctx, roomID, eventType, stateKey, outContent) + } + return +} + +// FullStateEvent gets a single state event in a room. Unlike [StateEvent], this gets the entire event +// (including details like the sender and timestamp). +// This requires the server to support the ?format=event query parameter, which is currently missing from the spec. +// See https://github.com/matrix-org/matrix-spec/issues/1047 for more info +func (cli *Client) FullStateEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, stateKey string) (evt *event.Event, err error) { + u := cli.BuildURLWithQuery(ClientURLPath{"v3", "rooms", roomID, "state", eventType.String(), stateKey}, map[string]string{ + "format": "event", + }) + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, &evt) + if evt != nil { + evt.Type.Class = event.StateEventType + _ = evt.Content.ParseRaw(evt.Type) + if evt.RoomID == "" { + evt.RoomID = roomID + } + } + if err == nil && cli.StateStore != nil { + UpdateStateStore(ctx, cli.StateStore, evt) + } + return +} + +// parseRoomStateArray parses a JSON array as a stream and stores the events inside it in a room state map. +func parseRoomStateArray(_ *http.Request, res *http.Response, responseJSON interface{}) ([]byte, error) { + response := make(RoomStateMap) + responsePtr := responseJSON.(*map[event.Type]map[string]*event.Event) + *responsePtr = response + dec := json.NewDecoder(res.Body) + + arrayStart, err := dec.Token() + if err != nil { + return nil, err + } else if arrayStart != json.Delim('[') { + return nil, fmt.Errorf("expected array start, got %+v", arrayStart) + } + + for i := 1; dec.More(); i++ { + var evt *event.Event + err = dec.Decode(&evt) + if err != nil { + return nil, fmt.Errorf("failed to parse state array item #%d: %v", i, err) + } + evt.Type.Class = event.StateEventType + _ = evt.Content.ParseRaw(evt.Type) + subMap, ok := response[evt.Type] + if !ok { + subMap = make(map[string]*event.Event) + response[evt.Type] = subMap + } + subMap[*evt.StateKey] = evt + } + + arrayEnd, err := dec.Token() + if err != nil { + return nil, err + } else if arrayEnd != json.Delim(']') { + return nil, fmt.Errorf("expected array end, got %+v", arrayStart) + } + return nil, nil +} + +// State gets all state in a room. +// See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidstate +func (cli *Client) State(ctx context.Context, roomID id.RoomID) (stateMap RoomStateMap, err error) { + _, err = cli.MakeFullRequest(ctx, FullRequest{ + Method: http.MethodGet, + URL: cli.BuildClientURL("v3", "rooms", roomID, "state"), + ResponseJSON: &stateMap, + Handler: parseRoomStateArray, + }) + if stateMap != nil { + pls, ok := stateMap[event.StatePowerLevels][""] + if ok { + pls.Content.AsPowerLevels().CreateEvent = stateMap[event.StateCreate][""] + } + } + if err == nil && cli.StateStore != nil { + for evtType, evts := range stateMap { + if evtType == event.StateMember { + continue + } + for _, evt := range evts { + if evt.RoomID == "" { + evt.RoomID = roomID + } + UpdateStateStore(ctx, cli.StateStore, evt) + } + } + updateErr := cli.StateStore.ReplaceCachedMembers(ctx, roomID, maps.Values(stateMap[event.StateMember])) + if updateErr != nil { + cli.cliOrContextLog(ctx).Warn().Err(updateErr). + Stringer("room_id", roomID). + Msg("Failed to update members in state store after fetching members") + } + } + return +} + +// StateAsArray gets all the state in a room as an array. It does not update the state store. +// Use State to get the events as a map and also update the state store. +func (cli *Client) StateAsArray(ctx context.Context, roomID id.RoomID) (state []*event.Event, err error) { + _, err = cli.MakeRequest(ctx, http.MethodGet, cli.BuildClientURL("v3", "rooms", roomID, "state"), nil, &state) + if err == nil { + for _, evt := range state { + evt.Type.Class = event.StateEventType + } + } + return +} + +// GetMediaConfig fetches the configuration of the content repository, such as upload limitations. +func (cli *Client) GetMediaConfig(ctx context.Context) (resp *RespMediaConfig, err error) { + _, err = cli.MakeRequest(ctx, http.MethodGet, cli.BuildClientURL("v1", "media", "config"), nil, &resp) + return +} + +func (cli *Client) RequestOpenIDToken(ctx context.Context) (resp *RespOpenIDToken, err error) { + _, err = cli.MakeRequest(ctx, http.MethodPost, cli.BuildClientURL("v3", "user", cli.UserID, "openid", "request_token"), nil, &resp) + return +} + +// UploadLink uploads an HTTP URL and then returns an MXC URI. +func (cli *Client) UploadLink(ctx context.Context, link string) (*RespMediaUpload, error) { + if cli == nil { + return nil, ErrClientIsNil + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, link, nil) + if err != nil { + return nil, err + } + + res, err := cli.Client.Do(req) + if res != nil { + defer res.Body.Close() + } + if err != nil { + return nil, err + } + return cli.Upload(ctx, res.Body, res.Header.Get("Content-Type"), res.ContentLength) +} + +func (cli *Client) Download(ctx context.Context, mxcURL id.ContentURI) (*http.Response, error) { + _, resp, err := cli.MakeFullRequestWithResp(ctx, FullRequest{ + Method: http.MethodGet, + URL: cli.BuildClientURL("v1", "media", "download", mxcURL.Homeserver, mxcURL.FileID), + DontReadResponse: true, + }) + return resp, err +} + +type DownloadThumbnailExtra struct { + Method string + Animated bool +} + +func (cli *Client) DownloadThumbnail(ctx context.Context, mxcURL id.ContentURI, height, width int, extras ...DownloadThumbnailExtra) (*http.Response, error) { + if len(extras) > 1 { + panic(fmt.Errorf("invalid number of arguments to DownloadThumbnail: %d", len(extras))) + } + var extra DownloadThumbnailExtra + if len(extras) == 1 { + extra = extras[0] + } + path := ClientURLPath{"v1", "media", "thumbnail", mxcURL.Homeserver, mxcURL.FileID} + query := map[string]string{ + "height": strconv.Itoa(height), + "width": strconv.Itoa(width), + } + if extra.Method != "" { + query["method"] = extra.Method + } + if extra.Animated { + query["animated"] = "true" + } + _, resp, err := cli.MakeFullRequestWithResp(ctx, FullRequest{ + Method: http.MethodGet, + URL: cli.BuildURLWithQuery(path, query), + DontReadResponse: true, + }) + return resp, err +} + +func (cli *Client) DownloadBytes(ctx context.Context, mxcURL id.ContentURI) ([]byte, error) { + resp, err := cli.Download(ctx, mxcURL) + if err != nil { + return nil, err + } + defer resp.Body.Close() + return io.ReadAll(resp.Body) +} + +type ReqCreateMXC struct { + BeeperUniqueID string + BeeperRoomID id.RoomID +} + +// CreateMXC creates a blank Matrix content URI to allow uploading the content asynchronously later. +// +// See https://spec.matrix.org/v1.7/client-server-api/#post_matrixmediav1create +func (cli *Client) CreateMXC(ctx context.Context, extra ...ReqCreateMXC) (*RespCreateMXC, error) { + var m RespCreateMXC + query := map[string]string{} + if len(extra) > 0 { + if extra[0].BeeperUniqueID != "" { + query["com.beeper.unique_id"] = extra[0].BeeperUniqueID + } + if extra[0].BeeperRoomID != "" { + query["com.beeper.room_id"] = string(extra[0].BeeperRoomID) + } + } + createURL := cli.BuildURLWithQuery(MediaURLPath{"v1", "create"}, query) + _, err := cli.MakeRequest(ctx, http.MethodPost, createURL, nil, &m) + return &m, err +} + +// UploadAsync creates a blank content URI with CreateMXC, starts uploading the data in the background +// and returns the created MXC immediately. +// +// See https://spec.matrix.org/v1.7/client-server-api/#post_matrixmediav1create +// and https://spec.matrix.org/v1.7/client-server-api/#put_matrixmediav3uploadservernamemediaid +func (cli *Client) UploadAsync(ctx context.Context, req ReqUploadMedia) (*RespCreateMXC, error) { + resp, err := cli.CreateMXC(ctx) + if err != nil { + req.DoneCallback() + return nil, err + } + req.MXC = resp.ContentURI + req.UnstableUploadURL = resp.UnstableUploadURL + go func() { + _, err = cli.UploadMedia(ctx, req) + if err != nil { + cli.Log.Error().Stringer("mxc", req.MXC).Err(err).Msg("Async upload of media failed") + } + }() + return resp, nil +} + +func (cli *Client) UploadBytes(ctx context.Context, data []byte, contentType string) (*RespMediaUpload, error) { + return cli.UploadBytesWithName(ctx, data, contentType, "") +} + +func (cli *Client) UploadBytesWithName(ctx context.Context, data []byte, contentType, fileName string) (*RespMediaUpload, error) { + return cli.UploadMedia(ctx, ReqUploadMedia{ + ContentBytes: data, + ContentType: contentType, + FileName: fileName, + }) +} + +// Upload uploads the given data to the content repository and returns an MXC URI. +// +// Deprecated: UploadMedia should be used instead. +func (cli *Client) Upload(ctx context.Context, content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { + return cli.UploadMedia(ctx, ReqUploadMedia{ + Content: content, + ContentLength: contentLength, + ContentType: contentType, + }) +} + +type ReqUploadMedia struct { + ContentBytes []byte + Content io.Reader + ContentLength int64 + ContentType string + FileName string + + DoneCallback func() + + // MXC specifies an existing MXC URI which doesn't have content yet to upload into. + // See https://spec.matrix.org/unstable/client-server-api/#put_matrixmediav3uploadservernamemediaid + MXC id.ContentURI + + // UnstableUploadURL specifies the URL to upload the content to. MXC must also be set. + // see https://github.com/matrix-org/matrix-spec-proposals/pull/3870 for more info + UnstableUploadURL string +} + +func (cli *Client) tryUploadMediaToURL(ctx context.Context, url, contentType string, content io.Reader, contentLength int64) (*http.Response, error) { + cli.Log.Debug().Str("url", url).Msg("Uploading media to external URL") + req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, content) + if err != nil { + return nil, err + } + req.ContentLength = contentLength + req.Header.Set("Content-Type", contentType) + if cli.UserAgent != "" { + req.Header.Set("User-Agent", cli.UserAgent+" (external media uploader)") + } + + if cli.ExternalClient != nil { + return cli.ExternalClient.Do(req) + } else { + return http.DefaultClient.Do(req) + } +} + +func (cli *Client) uploadMediaToURL(ctx context.Context, data ReqUploadMedia) (*RespMediaUpload, error) { + retries := cli.DefaultHTTPRetries + reader := data.Content + if data.ContentBytes != nil { + data.ContentLength = int64(len(data.ContentBytes)) + reader = bytes.NewReader(data.ContentBytes) + } else if rsc, ok := reader.(io.ReadSeekCloser); ok { + // Prevent HTTP from closing the request body, it might be needed for retries + reader = nopCloseSeeker{rsc} + } + readerSeeker, canSeek := reader.(io.ReadSeeker) + if !canSeek { + retries = 0 + } + for { + resp, err := cli.tryUploadMediaToURL(ctx, data.UnstableUploadURL, data.ContentType, reader, data.ContentLength) + if err == nil { + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + // Everything is fine + break + } + err = fmt.Errorf("HTTP %d", resp.StatusCode) + } else if errors.Is(err, context.Canceled) { + cli.Log.Warn().Str("url", data.UnstableUploadURL).Msg("External media upload canceled") + return nil, err + } + if retries <= 0 { + cli.Log.Warn().Str("url", data.UnstableUploadURL).Err(err). + Msg("Error uploading media to external URL, not retrying") + return nil, err + } + cli.Log.Warn().Str("url", data.UnstableUploadURL).Err(err). + Msg("Error uploading media to external URL, retrying") + retries-- + _, err = readerSeeker.Seek(0, io.SeekStart) + if err != nil { + return nil, fmt.Errorf("failed to seek back to start of reader: %w", err) + } + } + + query := map[string]string{} + if len(data.FileName) > 0 { + query["filename"] = data.FileName + } + + notifyURL := cli.BuildURLWithQuery(MediaURLPath{"unstable", "com.beeper.msc3870", "upload", data.MXC.Homeserver, data.MXC.FileID, "complete"}, query) + + var m *RespMediaUpload + _, err := cli.MakeRequest(ctx, http.MethodPost, notifyURL, nil, &m) + if err != nil { + return nil, err + } + + return m, nil +} + +type nopCloseSeeker struct { + io.ReadSeeker +} + +func (nopCloseSeeker) Close() error { + return nil +} + +// UploadMedia uploads the given data to the content repository and returns an MXC URI. +// See https://spec.matrix.org/v1.7/client-server-api/#post_matrixmediav3upload +func (cli *Client) UploadMedia(ctx context.Context, data ReqUploadMedia) (*RespMediaUpload, error) { + if data.DoneCallback != nil { + defer data.DoneCallback() + } + if cli == nil { + return nil, ErrClientIsNil + } + if data.UnstableUploadURL != "" { + if data.MXC.IsEmpty() { + return nil, errors.New("MXC must also be set when uploading to external URL") + } + return cli.uploadMediaToURL(ctx, data) + } + u, _ := url.Parse(cli.BuildURL(MediaURLPath{"v3", "upload"})) + method := http.MethodPost + if !data.MXC.IsEmpty() { + u, _ = url.Parse(cli.BuildURL(MediaURLPath{"v3", "upload", data.MXC.Homeserver, data.MXC.FileID})) + method = http.MethodPut + } + if len(data.FileName) > 0 { + q := u.Query() + q.Set("filename", data.FileName) + u.RawQuery = q.Encode() + } + + var headers http.Header + if len(data.ContentType) > 0 { + headers = http.Header{"Content-Type": []string{data.ContentType}} + } + + var m RespMediaUpload + _, err := cli.MakeFullRequest(ctx, FullRequest{ + Method: method, + URL: u.String(), + Headers: headers, + RequestBytes: data.ContentBytes, + RequestBody: data.Content, + RequestLength: data.ContentLength, + ResponseJSON: &m, + }) + return &m, err +} + +// GetURLPreview asks the homeserver to fetch a preview for a given URL. +// +// See https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3preview_url +func (cli *Client) GetURLPreview(ctx context.Context, url string) (*RespPreviewURL, error) { + reqURL := cli.BuildURLWithQuery(ClientURLPath{"v1", "media", "preview_url"}, map[string]string{ + "url": url, + }) + var output RespPreviewURL + _, err := cli.MakeRequest(ctx, http.MethodGet, reqURL, nil, &output) + return &output, err +} + +// JoinedMembers returns a map of joined room members. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidjoined_members +// +// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes. +// This API is primarily designed for application services which may want to efficiently look up joined members in a room. +func (cli *Client) JoinedMembers(ctx context.Context, roomID id.RoomID) (resp *RespJoinedMembers, err error) { + u := cli.BuildClientURL("v3", "rooms", roomID, "joined_members") + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, &resp) + if err == nil && cli.StateStore != nil { + fakeEvents := make([]*event.Event, len(resp.Joined)) + i := 0 + for userID, member := range resp.Joined { + fakeEvents[i] = &event.Event{ + StateKey: ptr.Ptr(userID.String()), + Type: event.StateMember, + RoomID: roomID, + Content: event.Content{Parsed: &event.MemberEventContent{ + Membership: event.MembershipJoin, + AvatarURL: id.ContentURIString(member.AvatarURL), + Displayname: member.DisplayName, + }}, + } + i++ + } + updateErr := cli.StateStore.ReplaceCachedMembers(ctx, roomID, fakeEvents, event.MembershipJoin) + if updateErr != nil { + cli.cliOrContextLog(ctx).Warn().Err(updateErr). + Stringer("room_id", roomID). + Msg("Failed to update members in state store after fetching joined members") + } + } + return +} + +func (cli *Client) Members(ctx context.Context, roomID id.RoomID, req ...ReqMembers) (resp *RespMembers, err error) { + var extra ReqMembers + if len(req) > 0 { + extra = req[0] + } + query := map[string]string{} + if len(extra.At) > 0 { + query["at"] = extra.At + } + if len(extra.Membership) > 0 { + query["membership"] = string(extra.Membership) + } + if len(extra.NotMembership) > 0 { + query["not_membership"] = string(extra.NotMembership) + } + u := cli.BuildURLWithQuery(ClientURLPath{"v3", "rooms", roomID, "members"}, query) + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, &resp) + if err == nil { + for _, evt := range resp.Chunk { + _ = evt.Content.ParseRaw(evt.Type) + } + } + if err == nil && cli.StateStore != nil { + var onlyMemberships []event.Membership + if extra.Membership != "" { + onlyMemberships = []event.Membership{extra.Membership} + } else if extra.NotMembership != "" { + onlyMemberships = []event.Membership{event.MembershipJoin, event.MembershipLeave, event.MembershipInvite, event.MembershipBan, event.MembershipKnock} + onlyMemberships = slices.DeleteFunc(onlyMemberships, func(m event.Membership) bool { + return m == extra.NotMembership + }) + } + updateErr := cli.StateStore.ReplaceCachedMembers(ctx, roomID, resp.Chunk, onlyMemberships...) + if updateErr != nil { + cli.cliOrContextLog(ctx).Warn().Err(updateErr). + Stringer("room_id", roomID). + Msg("Failed to update members in state store after fetching members") + } + } + return +} + +// JoinedRooms returns a list of rooms which the client is joined to. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3joined_rooms +// +// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes. +// This API is primarily designed for application services which may want to efficiently look up joined rooms. +func (cli *Client) JoinedRooms(ctx context.Context) (resp *RespJoinedRooms, err error) { + u := cli.BuildClientURL("v3", "joined_rooms") + _, err = cli.MakeRequest(ctx, http.MethodGet, u, nil, &resp) + return +} + +func (cli *Client) PublicRooms(ctx context.Context, req *ReqPublicRooms) (resp *RespPublicRooms, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "publicRooms"}, req.Query()) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// Hierarchy returns a list of rooms that are in the room's hierarchy. See https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy +// +// The hierarchy API is provided to walk the space tree and discover the rooms with their aesthetic details. works in a depth-first manner: +// when it encounters another space as a child it recurses into that space before returning non-space children. +// +// The second function parameter specifies query parameters to limit the response. No query parameters will be added if it's nil. +func (cli *Client) Hierarchy(ctx context.Context, roomID id.RoomID, req *ReqHierarchy) (resp *RespHierarchy, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v1", "rooms", roomID, "hierarchy"}, req.Query()) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// Messages returns a list of message and state events for a room. It uses +// pagination query parameters to paginate history in the room. +// See https://spec.matrix.org/v1.12/client-server-api/#get_matrixclientv3roomsroomidmessages +func (cli *Client) Messages(ctx context.Context, roomID id.RoomID, from, to string, dir Direction, filter *FilterPart, limit int) (resp *RespMessages, err error) { + query := map[string]string{ + "dir": string(dir), + } + if filter != nil { + filterJSON, err := json.Marshal(filter) + if err != nil { + return nil, err + } + query["filter"] = string(filterJSON) + } + if from != "" { + query["from"] = from + } + if to != "" { + query["to"] = to + } + if limit != 0 { + query["limit"] = strconv.Itoa(limit) + } + + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "rooms", roomID, "messages"}, query) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// TimestampToEvent finds the ID of the event closest to the given timestamp. +// +// See https://spec.matrix.org/v1.6/client-server-api/#get_matrixclientv1roomsroomidtimestamp_to_event +func (cli *Client) TimestampToEvent(ctx context.Context, roomID id.RoomID, timestamp time.Time, dir Direction) (resp *RespTimestampToEvent, err error) { + query := map[string]string{ + "ts": strconv.FormatInt(timestamp.UnixMilli(), 10), + "dir": string(dir), + } + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v1", "rooms", roomID, "timestamp_to_event"}, query) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// Context returns a number of events that happened just before and after the +// specified event. It use pagination query parameters to paginate history in +// the room. +// See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidcontexteventid +func (cli *Client) Context(ctx context.Context, roomID id.RoomID, eventID id.EventID, filter *FilterPart, limit int) (resp *RespContext, err error) { + query := map[string]string{} + if filter != nil { + filterJSON, err := json.Marshal(filter) + if err != nil { + return nil, err + } + query["filter"] = string(filterJSON) + } + if limit != 0 { + query["limit"] = strconv.Itoa(limit) + } + + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "rooms", roomID, "context", eventID}, query) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) GetEvent(ctx context.Context, roomID id.RoomID, eventID id.EventID) (resp *event.Event, err error) { + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "event", eventID) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) GetUnredactedEventContent(ctx context.Context, roomID id.RoomID, eventID id.EventID) (resp *event.Event, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "rooms", roomID, "event", eventID}, map[string]string{ + "fi.mau.msc2815.include_unredacted_content": "true", + }) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) GetRelations(ctx context.Context, roomID id.RoomID, eventID id.EventID, req *ReqGetRelations) (resp *RespGetRelations, err error) { + urlPath := cli.BuildURLWithQuery(append(ClientURLPath{"v1", "rooms", roomID, "relations", eventID}, req.PathSuffix()...), req.Query()) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) MarkRead(ctx context.Context, roomID id.RoomID, eventID id.EventID) (err error) { + return cli.SendReceipt(ctx, roomID, eventID, event.ReceiptTypeRead, nil) +} + +// MarkReadWithContent sends a read receipt including custom data. +// +// Deprecated: Use SendReceipt instead. +func (cli *Client) MarkReadWithContent(ctx context.Context, roomID id.RoomID, eventID id.EventID, content interface{}) (err error) { + return cli.SendReceipt(ctx, roomID, eventID, event.ReceiptTypeRead, content) +} + +// SendReceipt sends a receipt, usually specifically a read receipt. +// +// Passing nil as the content is safe, the library will automatically replace it with an empty JSON object. +// To mark a message in a specific thread as read, use pass a ReqSendReceipt as the content. +func (cli *Client) SendReceipt(ctx context.Context, roomID id.RoomID, eventID id.EventID, receiptType event.ReceiptType, content interface{}) (err error) { + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "receipt", receiptType, eventID) + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, content, nil) + return +} + +func (cli *Client) SetReadMarkers(ctx context.Context, roomID id.RoomID, content interface{}) (err error) { + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "read_markers") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, content, nil) + return +} + +func (cli *Client) SetBeeperInboxState(ctx context.Context, roomID id.RoomID, content *ReqSetBeeperInboxState) (err error) { + urlPath := cli.BuildClientURL("unstable", "com.beeper.inbox", "user", cli.UserID, "rooms", roomID, "inbox_state") + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, content, nil) + return +} + +func (cli *Client) AddTag(ctx context.Context, roomID id.RoomID, tag event.RoomTag, order float64) error { + return cli.AddTagWithCustomData(ctx, roomID, tag, &event.TagMetadata{ + Order: json.Number(strconv.FormatFloat(order, 'e', -1, 64)), + }) +} + +func (cli *Client) AddTagWithCustomData(ctx context.Context, roomID id.RoomID, tag event.RoomTag, data any) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "rooms", roomID, "tags", tag) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, data, nil) + return +} + +func (cli *Client) GetTags(ctx context.Context, roomID id.RoomID) (tags event.TagEventContent, err error) { + err = cli.GetTagsWithCustomData(ctx, roomID, &tags) + return +} + +func (cli *Client) GetTagsWithCustomData(ctx context.Context, roomID id.RoomID, resp any) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "rooms", roomID, "tags") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) RemoveTag(ctx context.Context, roomID id.RoomID, tag event.RoomTag) (err error) { + urlPath := cli.BuildClientURL("v3", "user", cli.UserID, "rooms", roomID, "tags", tag) + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, nil) + return +} + +// Deprecated: Synapse may not handle setting m.tag directly properly, so you should use the Add/RemoveTag methods instead. +func (cli *Client) SetTags(ctx context.Context, roomID id.RoomID, tags event.Tags) (err error) { + return cli.SetRoomAccountData(ctx, roomID, "m.tag", map[string]event.Tags{ + "tags": tags, + }) +} + +// TurnServer returns turn server details and credentials for the client to use when initiating calls. +// See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3voipturnserver +func (cli *Client) TurnServer(ctx context.Context) (resp *RespTurnServer, err error) { + urlPath := cli.BuildClientURL("v3", "voip", "turnServer") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) CreateAlias(ctx context.Context, alias id.RoomAlias, roomID id.RoomID) (resp *RespAliasCreate, err error) { + urlPath := cli.BuildClientURL("v3", "directory", "room", alias) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, &ReqAliasCreate{RoomID: roomID}, &resp) + return +} + +func (cli *Client) ResolveAlias(ctx context.Context, alias id.RoomAlias) (resp *RespAliasResolve, err error) { + urlPath := cli.BuildClientURL("v3", "directory", "room", alias) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) DeleteAlias(ctx context.Context, alias id.RoomAlias) (resp *RespAliasDelete, err error) { + urlPath := cli.BuildClientURL("v3", "directory", "room", alias) + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, &resp) + return +} + +func (cli *Client) GetAliases(ctx context.Context, roomID id.RoomID) (resp *RespAliasList, err error) { + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "aliases") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) UploadKeys(ctx context.Context, req *ReqUploadKeys) (resp *RespUploadKeys, err error) { + urlPath := cli.BuildClientURL("v3", "keys", "upload") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +func (cli *Client) QueryKeys(ctx context.Context, req *ReqQueryKeys) (resp *RespQueryKeys, err error) { + urlPath := cli.BuildClientURL("v3", "keys", "query") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +func (cli *Client) ClaimKeys(ctx context.Context, req *ReqClaimKeys) (resp *RespClaimKeys, err error) { + urlPath := cli.BuildClientURL("v3", "keys", "claim") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +func (cli *Client) GetKeyChanges(ctx context.Context, from, to string) (resp *RespKeyChanges, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "keys", "changes"}, map[string]string{ + "from": from, + "to": to, + }) + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, nil, &resp) + return +} + +// GetKeyBackup retrieves the keys from the backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keyskeys +func (cli *Client) GetKeyBackup(ctx context.Context, version id.KeyBackupVersion) (resp *RespRoomKeys[backup.EncryptedSessionData[backup.MegolmSessionData]], err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys"}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// PutKeysInBackup stores several keys in the backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#put_matrixclientv3room_keyskeys +func (cli *Client) PutKeysInBackup(ctx context.Context, version id.KeyBackupVersion, req *ReqKeyBackup) (resp *RespRoomKeysUpdate, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys"}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, req, &resp) + return +} + +// DeleteKeyBackup deletes all keys from the backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#delete_matrixclientv3room_keyskeys +func (cli *Client) DeleteKeyBackup(ctx context.Context, version id.KeyBackupVersion) (resp *RespRoomKeysUpdate, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys"}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, &resp) + return +} + +// GetKeyBackupForRoom retrieves the keys from the backup for the given room. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keyskeysroomid +func (cli *Client) GetKeyBackupForRoom( + ctx context.Context, version id.KeyBackupVersion, roomID id.RoomID, +) (resp *RespRoomKeyBackup[backup.EncryptedSessionData[backup.MegolmSessionData]], err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String()}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// PutKeysInBackupForRoom stores several keys in the backup for the given room. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#put_matrixclientv3room_keyskeysroomid +func (cli *Client) PutKeysInBackupForRoom(ctx context.Context, version id.KeyBackupVersion, roomID id.RoomID, req *ReqRoomKeyBackup) (resp *RespRoomKeysUpdate, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String()}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, req, &resp) + return +} + +// DeleteKeysFromBackupForRoom deletes all the keys in the backup for the given +// room. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#delete_matrixclientv3room_keyskeysroomid +func (cli *Client) DeleteKeysFromBackupForRoom(ctx context.Context, version id.KeyBackupVersion, roomID id.RoomID) (resp *RespRoomKeysUpdate, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String()}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, &resp) + return +} + +// GetKeyBackupForRoomAndSession retrieves a key from the backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keyskeysroomidsessionid +func (cli *Client) GetKeyBackupForRoomAndSession( + ctx context.Context, version id.KeyBackupVersion, roomID id.RoomID, sessionID id.SessionID, +) (resp *RespKeyBackupData[backup.EncryptedSessionData[backup.MegolmSessionData]], err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String(), sessionID.String()}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// PutKeysInBackupForRoomAndSession stores a key in the backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#put_matrixclientv3room_keyskeysroomidsessionid +func (cli *Client) PutKeysInBackupForRoomAndSession(ctx context.Context, version id.KeyBackupVersion, roomID id.RoomID, sessionID id.SessionID, req *ReqKeyBackupData) (resp *RespRoomKeysUpdate, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String(), sessionID.String()}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, req, &resp) + return +} + +// DeleteKeysInBackupForRoomAndSession deletes a key from the backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#delete_matrixclientv3room_keyskeysroomidsessionid +func (cli *Client) DeleteKeysInBackupForRoomAndSession(ctx context.Context, version id.KeyBackupVersion, roomID id.RoomID, sessionID id.SessionID) (resp *RespRoomKeysUpdate, err error) { + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String(), sessionID.String()}, map[string]string{ + "version": string(version), + }) + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, &resp) + return +} + +// GetKeyBackupLatestVersion returns information about the latest backup version. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keysversion +func (cli *Client) GetKeyBackupLatestVersion(ctx context.Context) (resp *RespRoomKeysVersion[backup.MegolmAuthData], err error) { + urlPath := cli.BuildClientURL("v3", "room_keys", "version") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// CreateKeyBackupVersion creates a new key backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3room_keysversion +func (cli *Client) CreateKeyBackupVersion(ctx context.Context, req *ReqRoomKeysVersionCreate[backup.MegolmAuthData]) (resp *RespRoomKeysVersionCreate, err error) { + urlPath := cli.BuildClientURL("v3", "room_keys", "version") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +// GetKeyBackupVersion returns information about an existing key backup. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keysversionversion +func (cli *Client) GetKeyBackupVersion(ctx context.Context, version id.KeyBackupVersion) (resp *RespRoomKeysVersion[backup.MegolmAuthData], err error) { + urlPath := cli.BuildClientURL("v3", "room_keys", "version", version) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +// UpdateKeyBackupVersion updates information about an existing key backup. Only +// the auth_data can be modified. +// +// See: https://spec.matrix.org/v1.9/client-server-api/#put_matrixclientv3room_keysversionversion +func (cli *Client) UpdateKeyBackupVersion(ctx context.Context, version id.KeyBackupVersion, req *ReqRoomKeysVersionUpdate[backup.MegolmAuthData]) error { + urlPath := cli.BuildClientURL("v3", "room_keys", "version", version) + _, err := cli.MakeRequest(ctx, http.MethodPut, urlPath, nil, nil) + return err +} + +// DeleteKeyBackupVersion deletes an existing key backup. Both the information +// about the backup, as well as all key data related to the backup will be +// deleted. +// +// See: https://spec.matrix.org/v1.1/client-server-api/#delete_matrixclientv3room_keysversionversion +func (cli *Client) DeleteKeyBackupVersion(ctx context.Context, version id.KeyBackupVersion) error { + urlPath := cli.BuildClientURL("v3", "room_keys", "version", version) + _, err := cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, nil) + return err +} + +func (cli *Client) SendToDevice(ctx context.Context, eventType event.Type, req *ReqSendToDevice) (resp *RespSendToDevice, err error) { + urlPath := cli.BuildClientURL("v3", "sendToDevice", eventType.String(), cli.TxnID()) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, req, &resp) + return +} + +func (cli *Client) GetDevicesInfo(ctx context.Context) (resp *RespDevicesInfo, err error) { + urlPath := cli.BuildClientURL("v3", "devices") + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) GetDeviceInfo(ctx context.Context, deviceID id.DeviceID) (resp *RespDeviceInfo, err error) { + urlPath := cli.BuildClientURL("v3", "devices", deviceID) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + return +} + +func (cli *Client) SetDeviceInfo(ctx context.Context, deviceID id.DeviceID, req *ReqDeviceInfo) error { + urlPath := cli.BuildClientURL("v3", "devices", deviceID) + _, err := cli.MakeRequest(ctx, http.MethodPut, urlPath, req, nil) + return err +} + +func (cli *Client) DeleteDevice(ctx context.Context, deviceID id.DeviceID, req *ReqDeleteDevice) error { + urlPath := cli.BuildClientURL("v3", "devices", deviceID) + _, err := cli.MakeRequest(ctx, http.MethodDelete, urlPath, req, nil) + return err +} + +func (cli *Client) DeleteDevices(ctx context.Context, req *ReqDeleteDevices) error { + urlPath := cli.BuildClientURL("v3", "delete_devices") + _, err := cli.MakeRequest(ctx, http.MethodPost, urlPath, req, nil) + return err +} + +type UIACallback = func(*RespUserInteractive) interface{} + +// UploadCrossSigningKeys uploads the given cross-signing keys to the server. +// Because the endpoint requires user-interactive authentication a callback must be provided that, +// given the UI auth parameters, produces the required result (or nil to end the flow). +func (cli *Client) UploadCrossSigningKeys(ctx context.Context, keys *UploadCrossSigningKeysReq, uiaCallback UIACallback) error { + content, err := cli.MakeFullRequest(ctx, FullRequest{ + Method: http.MethodPost, + URL: cli.BuildClientURL("v3", "keys", "device_signing", "upload"), + RequestJSON: keys, + SensitiveContent: keys.Auth != nil, + }) + if respErr, ok := err.(HTTPError); ok && respErr.IsStatus(http.StatusUnauthorized) && uiaCallback != nil { + // try again with UI auth + var uiAuthResp RespUserInteractive + if err := json.Unmarshal(content, &uiAuthResp); err != nil { + return fmt.Errorf("failed to decode UIA response: %w", err) + } + auth := uiaCallback(&uiAuthResp) + if auth != nil { + keys.Auth = auth + return cli.UploadCrossSigningKeys(ctx, keys, nil) + } + } + return err +} + +func (cli *Client) UploadSignatures(ctx context.Context, req *ReqUploadSignatures) (resp *RespUploadSignatures, err error) { + urlPath := cli.BuildClientURL("v3", "keys", "signatures", "upload") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +// GetPushRules returns the push notification rules for the global scope. +func (cli *Client) GetPushRules(ctx context.Context) (*pushrules.PushRuleset, error) { + return cli.GetScopedPushRules(ctx, "global") +} + +// GetScopedPushRules returns the push notification rules for the given scope. +func (cli *Client) GetScopedPushRules(ctx context.Context, scope string) (resp *pushrules.PushRuleset, err error) { + u, _ := url.Parse(cli.BuildClientURL("v3", "pushrules", scope)) + // client.BuildURL returns the URL without a trailing slash, but the pushrules endpoint requires the slash. + u.Path += "/" + _, err = cli.MakeRequest(ctx, http.MethodGet, u.String(), nil, &resp) + return +} + +func (cli *Client) GetPushRule(ctx context.Context, scope string, kind pushrules.PushRuleType, ruleID string) (resp *pushrules.PushRule, err error) { + urlPath := cli.BuildClientURL("v3", "pushrules", scope, kind, ruleID) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) + if resp != nil { + resp.Type = kind + } + return +} + +func (cli *Client) DeletePushRule(ctx context.Context, scope string, kind pushrules.PushRuleType, ruleID string) error { + urlPath := cli.BuildClientURL("v3", "pushrules", scope, kind, ruleID) + _, err := cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, nil) + return err +} + +func (cli *Client) PutPushRule(ctx context.Context, scope string, kind pushrules.PushRuleType, ruleID string, req *ReqPutPushRule) error { + query := make(map[string]string) + if len(req.After) > 0 { + query["after"] = req.After + } + if len(req.Before) > 0 { + query["before"] = req.Before + } + urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "pushrules", scope, kind, ruleID}, query) + _, err := cli.MakeRequest(ctx, http.MethodPut, urlPath, req, nil) + return err +} + +func (cli *Client) ReportEvent(ctx context.Context, roomID id.RoomID, eventID id.EventID, reason string) error { + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "report", eventID) + _, err := cli.MakeRequest(ctx, http.MethodPost, urlPath, &ReqReport{Reason: reason, Score: -100}, nil) + return err +} + +func (cli *Client) ReportRoom(ctx context.Context, roomID id.RoomID, reason string) error { + urlPath := cli.BuildClientURL("v3", "rooms", roomID, "report") + _, err := cli.MakeRequest(ctx, http.MethodPost, urlPath, &ReqReport{Reason: reason, Score: -100}, nil) + return err +} + +// UnstableGetSuspendedStatus uses MSC4323 to check if a user is suspended. +func (cli *Client) UnstableGetSuspendedStatus(ctx context.Context, userID id.UserID) (res *RespSuspended, err error) { + urlPath := cli.BuildClientURL("unstable", "uk.timedout.msc4323", "admin", "suspend", userID) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, res) + return +} + +// UnstableGetLockStatus uses MSC4323 to check if a user is locked. +func (cli *Client) UnstableGetLockStatus(ctx context.Context, userID id.UserID) (res *RespLocked, err error) { + urlPath := cli.BuildClientURL("unstable", "uk.timedout.msc4323", "admin", "lock", userID) + _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, res) + return +} + +// UnstableSetSuspendedStatus uses MSC4323 to set whether a user account is suspended. +func (cli *Client) UnstableSetSuspendedStatus(ctx context.Context, userID id.UserID, suspended bool) (res *RespSuspended, err error) { + urlPath := cli.BuildClientURL("unstable", "uk.timedout.msc4323", "admin", "suspend", userID) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, &ReqSuspend{Suspended: suspended}, res) + return +} + +// UnstableSetLockStatus uses MSC4323 to set whether a user account is locked. +func (cli *Client) UnstableSetLockStatus(ctx context.Context, userID id.UserID, locked bool) (res *RespLocked, err error) { + urlPath := cli.BuildClientURL("unstable", "uk.timedout.msc4323", "admin", "lock", userID) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, &ReqLocked{Locked: locked}, res) + return +} + +func (cli *Client) AppservicePing(ctx context.Context, id, txnID string) (resp *RespAppservicePing, err error) { + _, err = cli.MakeFullRequest(ctx, FullRequest{ + Method: http.MethodPost, + URL: cli.BuildClientURL("v1", "appservice", id, "ping"), + RequestJSON: &ReqAppservicePing{TxnID: txnID}, + ResponseJSON: &resp, + // This endpoint intentionally returns 50x, so don't retry + MaxAttempts: 1, + }) + return +} + +func (cli *Client) BeeperBatchSend(ctx context.Context, roomID id.RoomID, req *ReqBeeperBatchSend) (resp *RespBeeperBatchSend, err error) { + u := cli.BuildClientURL("unstable", "com.beeper.backfill", "rooms", roomID, "batch_send") + _, err = cli.MakeRequest(ctx, http.MethodPost, u, req, &resp) + return +} + +func (cli *Client) BeeperMergeRooms(ctx context.Context, req *ReqBeeperMergeRoom) (resp *RespBeeperMergeRoom, err error) { + urlPath := cli.BuildClientURL("unstable", "com.beeper.chatmerging", "merge") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} + +func (cli *Client) BeeperSplitRoom(ctx context.Context, req *ReqBeeperSplitRoom) (resp *RespBeeperSplitRoom, err error) { + urlPath := cli.BuildClientURL("unstable", "com.beeper.chatmerging", "rooms", req.RoomID, "split") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp) + return +} +func (cli *Client) BeeperDeleteRoom(ctx context.Context, roomID id.RoomID) (err error) { + urlPath := cli.BuildClientURL("unstable", "com.beeper.yeet", "rooms", roomID, "delete") + _, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, nil, nil) + return +} + +// TxnID returns the next transaction ID. +func (cli *Client) TxnID() string { + if cli == nil { + return "client is nil" + } + txnID := atomic.AddInt32(&cli.txnID, 1) + return fmt.Sprintf("mautrix-go_%d_%d", time.Now().UnixNano(), txnID) +} + +// NewClient creates a new Matrix Client ready for syncing +func NewClient(homeserverURL string, userID id.UserID, accessToken string) (*Client, error) { + hsURL, err := ParseAndNormalizeBaseURL(homeserverURL) + if err != nil { + return nil, err + } + return &Client{ + AccessToken: accessToken, + UserAgent: DefaultUserAgent, + HomeserverURL: hsURL, + UserID: userID, + Client: &http.Client{Timeout: 180 * time.Second}, + Syncer: NewDefaultSyncer(), + Log: zerolog.Nop(), + // By default, use an in-memory store which will never save filter ids / next batch tokens to disk. + // The client will work with this storer: it just won't remember across restarts. + // In practice, a database backend should be used. + Store: NewMemorySyncStore(), + }, nil +} diff --git a/vendor/maunium.net/go/mautrix/crypto/aescbc/aes_cbc.go b/vendor/maunium.net/go/mautrix/crypto/aescbc/aes_cbc.go new file mode 100644 index 00000000..d69a5f49 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/aescbc/aes_cbc.go @@ -0,0 +1,60 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package aescbc + +import ( + "crypto/aes" + "crypto/cipher" + + "maunium.net/go/mautrix/crypto/pkcs7" +) + +// Encrypt encrypts the plaintext with the key and IV. The IV length must be +// equal to the AES block size. +// +// This function might mutate the plaintext. +func Encrypt(key, iv, plaintext []byte) ([]byte, error) { + if len(key) == 0 { + return nil, ErrNoKeyProvided + } + if len(iv) != aes.BlockSize { + return nil, ErrIVNotBlockSize + } + plaintext = pkcs7.Pad(plaintext, aes.BlockSize) + + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + cipher.NewCBCEncrypter(block, iv).CryptBlocks(plaintext, plaintext) + return plaintext, nil +} + +// Decrypt decrypts the ciphertext with the key and IV. The IV length must be +// equal to the block size. +// +// This function mutates the ciphertext. +func Decrypt(key, iv, ciphertext []byte) ([]byte, error) { + if len(key) == 0 { + return nil, ErrNoKeyProvided + } + if len(iv) != aes.BlockSize { + return nil, ErrIVNotBlockSize + } + + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + if len(ciphertext) < aes.BlockSize { + return nil, ErrNotMultipleBlockSize + } + + cipher.NewCBCDecrypter(block, iv).CryptBlocks(ciphertext, ciphertext) + return pkcs7.Unpad(ciphertext), nil +} diff --git a/vendor/maunium.net/go/mautrix/crypto/aescbc/errors.go b/vendor/maunium.net/go/mautrix/crypto/aescbc/errors.go new file mode 100644 index 00000000..f3d2d7ce --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/aescbc/errors.go @@ -0,0 +1,15 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package aescbc + +import "errors" + +var ( + ErrNoKeyProvided = errors.New("no key") + ErrIVNotBlockSize = errors.New("IV length does not match AES block size") + ErrNotMultipleBlockSize = errors.New("ciphertext length is not a multiple of the AES block size") +) diff --git a/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go b/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go new file mode 100644 index 00000000..155cca5c --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/attachment/attachments.go @@ -0,0 +1,306 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package attachment + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "hash" + "io" + + "maunium.net/go/mautrix/crypto/utils" +) + +var ( + HashMismatch = errors.New("mismatching SHA-256 digest") + UnsupportedVersion = errors.New("unsupported Matrix file encryption version") + UnsupportedAlgorithm = errors.New("unsupported JWK encryption algorithm") + InvalidKey = errors.New("failed to decode key") + InvalidInitVector = errors.New("failed to decode initialization vector") + InvalidHash = errors.New("failed to decode SHA-256 hash") + ReaderClosed = errors.New("encrypting reader was already closed") +) + +var ( + keyBase64Length = base64.RawURLEncoding.EncodedLen(utils.AESCTRKeyLength) + ivBase64Length = base64.RawStdEncoding.EncodedLen(utils.AESCTRIVLength) + hashBase64Length = base64.RawStdEncoding.EncodedLen(utils.SHAHashLength) +) + +type JSONWebKey struct { + Key string `json:"k"` + Algorithm string `json:"alg"` + Extractable bool `json:"ext"` + KeyType string `json:"kty"` + KeyOps []string `json:"key_ops"` +} + +type EncryptedFileHashes struct { + SHA256 string `json:"sha256"` +} + +type decodedKeys struct { + key [utils.AESCTRKeyLength]byte + iv [utils.AESCTRIVLength]byte + + sha256 [utils.SHAHashLength]byte +} + +type EncryptedFile struct { + Key JSONWebKey `json:"key"` + InitVector string `json:"iv"` + Hashes EncryptedFileHashes `json:"hashes"` + Version string `json:"v"` + + decoded *decodedKeys +} + +func NewEncryptedFile() *EncryptedFile { + key, iv := utils.GenAttachmentA256CTR() + return &EncryptedFile{ + Key: JSONWebKey{ + Key: base64.RawURLEncoding.EncodeToString(key[:]), + Algorithm: "A256CTR", + Extractable: true, + KeyType: "oct", + KeyOps: []string{"encrypt", "decrypt"}, + }, + InitVector: base64.RawStdEncoding.EncodeToString(iv[:]), + Version: "v2", + + decoded: &decodedKeys{key: key, iv: iv}, + } +} + +func (ef *EncryptedFile) decodeKeys(includeHash bool) error { + if ef.decoded != nil { + return nil + } else if len(ef.Key.Key) != keyBase64Length { + return InvalidKey + } else if len(ef.InitVector) != ivBase64Length { + return InvalidInitVector + } else if includeHash && len(ef.Hashes.SHA256) != hashBase64Length { + return InvalidHash + } + ef.decoded = &decodedKeys{} + _, err := base64.RawURLEncoding.Decode(ef.decoded.key[:], []byte(ef.Key.Key)) + if err != nil { + return InvalidKey + } + _, err = base64.RawStdEncoding.Decode(ef.decoded.iv[:], []byte(ef.InitVector)) + if err != nil { + return InvalidInitVector + } + if includeHash { + _, err = base64.RawStdEncoding.Decode(ef.decoded.sha256[:], []byte(ef.Hashes.SHA256)) + if err != nil { + return InvalidHash + } + } + return nil +} + +// Encrypt encrypts the given data, updates the SHA256 hash in the EncryptedFile struct and returns the ciphertext. +// +// Deprecated: this makes a copy for the ciphertext, which means 2x memory usage. EncryptInPlace is recommended. +func (ef *EncryptedFile) Encrypt(plaintext []byte) []byte { + ciphertext := make([]byte, len(plaintext)) + copy(ciphertext, plaintext) + ef.EncryptInPlace(ciphertext) + return ciphertext +} + +// EncryptInPlace encrypts the given data in-place (i.e. the provided data is overridden with the ciphertext) +// and updates the SHA256 hash in the EncryptedFile struct. +func (ef *EncryptedFile) EncryptInPlace(data []byte) { + ef.decodeKeys(false) + utils.XorA256CTR(data, ef.decoded.key, ef.decoded.iv) + checksum := sha256.Sum256(data) + ef.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(checksum[:]) +} + +type ReadWriterAt interface { + io.WriterAt + io.Reader +} + +// EncryptFile encrypts the given file in-place and updates the SHA256 hash in the EncryptedFile struct. +func (ef *EncryptedFile) EncryptFile(file ReadWriterAt) error { + err := ef.decodeKeys(false) + if err != nil { + return err + } + block, _ := aes.NewCipher(ef.decoded.key[:]) + stream := cipher.NewCTR(block, ef.decoded.iv[:]) + hasher := sha256.New() + buf := make([]byte, 32*1024) + var writePtr int64 + var n int + for { + n, err = file.Read(buf) + if err != nil && !errors.Is(err, io.EOF) { + return err + } + if n == 0 { + break + } + stream.XORKeyStream(buf[:n], buf[:n]) + _, err = file.WriteAt(buf[:n], writePtr) + if err != nil { + return err + } + writePtr += int64(n) + hasher.Write(buf[:n]) + } + ef.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(hasher.Sum(nil)) + return nil +} + +type encryptingReader struct { + stream cipher.Stream + hash hash.Hash + source io.Reader + file *EncryptedFile + closed bool + + isDecrypting bool +} + +var _ io.ReadSeekCloser = (*encryptingReader)(nil) + +func (r *encryptingReader) Seek(offset int64, whence int) (int64, error) { + if r.closed { + return 0, ReaderClosed + } + if offset != 0 || whence != io.SeekStart { + return 0, fmt.Errorf("attachments.EncryptStream: only seeking to the beginning is supported") + } + seeker, ok := r.source.(io.ReadSeeker) + if !ok { + return 0, fmt.Errorf("attachments.EncryptStream: source reader (%T) is not an io.ReadSeeker", r.source) + } + n, err := seeker.Seek(offset, whence) + if err != nil { + return 0, err + } + block, _ := aes.NewCipher(r.file.decoded.key[:]) + r.stream = cipher.NewCTR(block, r.file.decoded.iv[:]) + r.hash.Reset() + return n, nil +} + +func (r *encryptingReader) Read(dst []byte) (n int, err error) { + if r.closed { + return 0, ReaderClosed + } else if r.isDecrypting && r.file.decoded == nil { + if err = r.file.PrepareForDecryption(); err != nil { + return + } + } + n, err = r.source.Read(dst) + if r.isDecrypting { + r.hash.Write(dst[:n]) + } + r.stream.XORKeyStream(dst[:n], dst[:n]) + if !r.isDecrypting { + r.hash.Write(dst[:n]) + } + return +} + +func (r *encryptingReader) Close() (err error) { + closer, ok := r.source.(io.ReadCloser) + if ok { + err = closer.Close() + } + if r.isDecrypting { + if !hmac.Equal(r.hash.Sum(nil), r.file.decoded.sha256[:]) { + return HashMismatch + } + } else { + r.file.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(r.hash.Sum(nil)) + } + r.closed = true + return +} + +// EncryptStream wraps the given io.Reader in order to encrypt the data. +// +// The Close() method of the returned io.ReadCloser must be called for the SHA256 hash +// in the EncryptedFile struct to be updated. The metadata is not valid before the hash +// is filled. +func (ef *EncryptedFile) EncryptStream(reader io.Reader) io.ReadSeekCloser { + ef.decodeKeys(false) + block, _ := aes.NewCipher(ef.decoded.key[:]) + return &encryptingReader{ + stream: cipher.NewCTR(block, ef.decoded.iv[:]), + hash: sha256.New(), + source: reader, + file: ef, + } +} + +// Decrypt decrypts the given data and returns the plaintext. +// +// Deprecated: this makes a copy for the plaintext data, which means 2x memory usage. DecryptInPlace is recommended. +func (ef *EncryptedFile) Decrypt(ciphertext []byte) ([]byte, error) { + plaintext := make([]byte, len(ciphertext)) + copy(plaintext, ciphertext) + return plaintext, ef.DecryptInPlace(plaintext) +} + +// PrepareForDecryption checks that the version and algorithm are supported and decodes the base64 keys +// +// DecryptStream will call this with the first Read() call if this hasn't been called manually. +// +// DecryptInPlace will always call this automatically, so calling this manually is not necessary when using that function. +func (ef *EncryptedFile) PrepareForDecryption() error { + if ef.Version != "v2" { + return UnsupportedVersion + } else if ef.Key.Algorithm != "A256CTR" { + return UnsupportedAlgorithm + } else if err := ef.decodeKeys(true); err != nil { + return err + } + return nil +} + +// DecryptInPlace decrypts the given data in-place (i.e. the provided data is overridden with the plaintext). +func (ef *EncryptedFile) DecryptInPlace(data []byte) error { + if err := ef.PrepareForDecryption(); err != nil { + return err + } + dataHash := sha256.Sum256(data) + if !hmac.Equal(ef.decoded.sha256[:], dataHash[:]) { + return HashMismatch + } + utils.XorA256CTR(data, ef.decoded.key, ef.decoded.iv) + return nil +} + +// DecryptStream wraps the given io.Reader in order to decrypt the data. +// +// The first Read call will check the algorithm and decode keys, so it might return an error before actually reading anything. +// If you want to validate the file before opening the stream, call PrepareForDecryption manually and check for errors. +// +// The Close call will validate the hash and return an error if it doesn't match. +// In this case, the written data should be considered compromised and should not be used further. +func (ef *EncryptedFile) DecryptStream(reader io.Reader) io.ReadSeekCloser { + block, _ := aes.NewCipher(ef.decoded.key[:]) + return &encryptingReader{ + isDecrypting: true, + stream: cipher.NewCTR(block, ef.decoded.iv[:]), + hash: sha256.New(), + source: reader, + file: ef, + } +} diff --git a/vendor/maunium.net/go/mautrix/crypto/backup/encryptedsessiondata.go b/vendor/maunium.net/go/mautrix/crypto/backup/encryptedsessiondata.go new file mode 100644 index 00000000..25250178 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/backup/encryptedsessiondata.go @@ -0,0 +1,135 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package backup + +import ( + "bytes" + "crypto/ecdh" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "encoding/json" + "errors" + + "go.mau.fi/util/jsonbytes" + "golang.org/x/crypto/hkdf" + + "maunium.net/go/mautrix/crypto/aescbc" +) + +var ErrInvalidMAC = errors.New("invalid MAC") + +// EncryptedSessionData is the encrypted session_data field of a key backup as +// defined in [Section 11.12.3.2.2 of the Spec]. +// +// The type parameter T represents the format of the session data contained in +// the encrypted payload. +// +// [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +type EncryptedSessionData[T any] struct { + Ciphertext jsonbytes.UnpaddedBytes `json:"ciphertext"` + Ephemeral EphemeralKey `json:"ephemeral"` + MAC jsonbytes.UnpaddedBytes `json:"mac"` +} + +func calculateEncryptionParameters(sharedSecret []byte) (key, macKey, iv []byte, err error) { + hkdfReader := hkdf.New(sha256.New, sharedSecret, nil, nil) + encryptionParams := make([]byte, 80) + _, err = hkdfReader.Read(encryptionParams) + if err != nil { + return nil, nil, nil, err + } + + return encryptionParams[:32], encryptionParams[32:64], encryptionParams[64:], nil +} + +// calculateCompatMAC calculates the MAC as described in step 5 of according to +// [Section 11.12.3.2.2] of the Spec which was updated in spec version 1.10 to +// reflect what is actually implemented in libolm and Vodozemac. +// +// Libolm implemented the MAC functionality incorrectly. The MAC is computed +// over an empty string rather than the ciphertext. Vodozemac implemented this +// functionality the same way as libolm for compatibility. In version 1.10 of +// the spec, the description of step 5 was updated to reflect the de-facto +// standard of libolm and Vodozemac. +// +// [Section 11.12.3.2.2]: https://spec.matrix.org/v1.11/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +func calculateCompatMAC(macKey []byte) []byte { + hash := hmac.New(sha256.New, macKey) + return hash.Sum(nil)[:8] +} + +// EncryptSessionData encrypts the given session data with the given recovery +// key as defined in [Section 11.12.3.2.2 of the Spec]. +// +// [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +func EncryptSessionData[T any](backupKey *MegolmBackupKey, sessionData T) (*EncryptedSessionData[T], error) { + return EncryptSessionDataWithPubkey(backupKey.PublicKey(), sessionData) +} + +func EncryptSessionDataWithPubkey[T any](pubkey *ecdh.PublicKey, sessionData T) (*EncryptedSessionData[T], error) { + sessionJSON, err := json.Marshal(sessionData) + if err != nil { + return nil, err + } + + ephemeralKey, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + + sharedSecret, err := ephemeralKey.ECDH(pubkey) + if err != nil { + return nil, err + } + + key, macKey, iv, err := calculateEncryptionParameters(sharedSecret) + if err != nil { + return nil, err + } + + ciphertext, err := aescbc.Encrypt(key, iv, sessionJSON) + if err != nil { + return nil, err + } + + return &EncryptedSessionData[T]{ + Ciphertext: ciphertext, + Ephemeral: EphemeralKey{ephemeralKey.PublicKey()}, + MAC: calculateCompatMAC(macKey), + }, nil +} + +// Decrypt decrypts the [EncryptedSessionData] into a *T using the recovery key +// by reversing the process described in [Section 11.12.3.2.2 of the Spec]. +// +// [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +func (esd *EncryptedSessionData[T]) Decrypt(backupKey *MegolmBackupKey) (*T, error) { + sharedSecret, err := backupKey.ECDH(esd.Ephemeral.PublicKey) + if err != nil { + return nil, err + } + + key, macKey, iv, err := calculateEncryptionParameters(sharedSecret) + if err != nil { + return nil, err + } + + // Verify the MAC before decrypting. + if !bytes.Equal(calculateCompatMAC(macKey), esd.MAC) { + return nil, ErrInvalidMAC + } + + plaintext, err := aescbc.Decrypt(key, iv, esd.Ciphertext) + if err != nil { + return nil, err + } + + var sessionData T + err = json.Unmarshal(plaintext, &sessionData) + return &sessionData, err +} diff --git a/vendor/maunium.net/go/mautrix/crypto/backup/ephemeralkey.go b/vendor/maunium.net/go/mautrix/crypto/backup/ephemeralkey.go new file mode 100644 index 00000000..e481e7a9 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/backup/ephemeralkey.go @@ -0,0 +1,41 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package backup + +import ( + "crypto/ecdh" + "encoding/base64" + "encoding/json" +) + +// EphemeralKey is a wrapper around an ECDH X25519 public key that implements +// JSON marshalling and unmarshalling. +type EphemeralKey struct { + *ecdh.PublicKey +} + +func (k *EphemeralKey) MarshalJSON() ([]byte, error) { + if k == nil || k.PublicKey == nil { + return json.Marshal(nil) + } + return json.Marshal(base64.RawStdEncoding.EncodeToString(k.Bytes())) +} + +func (k *EphemeralKey) UnmarshalJSON(data []byte) error { + var keyStr string + err := json.Unmarshal(data, &keyStr) + if err != nil { + return err + } + + keyBytes, err := base64.RawStdEncoding.DecodeString(keyStr) + if err != nil { + return err + } + k.PublicKey, err = ecdh.X25519().NewPublicKey(keyBytes) + return err +} diff --git a/vendor/maunium.net/go/mautrix/crypto/backup/megolmbackup.go b/vendor/maunium.net/go/mautrix/crypto/backup/megolmbackup.go new file mode 100644 index 00000000..71b8e88b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/backup/megolmbackup.go @@ -0,0 +1,39 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package backup + +import ( + "maunium.net/go/mautrix/crypto/signatures" + "maunium.net/go/mautrix/id" +) + +// MegolmAuthData is the auth_data when the key backup is created with +// the [id.KeyBackupAlgorithmMegolmBackupV1] algorithm as defined in +// [Section 11.12.3.2.2 of the Spec]. +// +// [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +type MegolmAuthData struct { + PublicKey id.Ed25519 `json:"public_key"` + Signatures signatures.Signatures `json:"signatures"` +} + +type SenderClaimedKeys struct { + Ed25519 id.Ed25519 `json:"ed25519"` +} + +// MegolmSessionData is the decrypted session_data when the key backup is created +// with the [id.KeyBackupAlgorithmMegolmBackupV1] algorithm as defined in +// [Section 11.12.3.2.2 of the Spec]. +// +// [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +type MegolmSessionData struct { + Algorithm id.Algorithm `json:"algorithm"` + ForwardingKeyChain []string `json:"forwarding_curve25519_key_chain"` + SenderClaimedKeys SenderClaimedKeys `json:"sender_claimed_keys"` + SenderKey id.SenderKey `json:"sender_key"` + SessionKey string `json:"session_key"` +} diff --git a/vendor/maunium.net/go/mautrix/crypto/backup/megolmbackupkey.go b/vendor/maunium.net/go/mautrix/crypto/backup/megolmbackupkey.go new file mode 100644 index 00000000..8f23d104 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/backup/megolmbackupkey.go @@ -0,0 +1,34 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package backup + +import ( + "crypto/ecdh" + "crypto/rand" +) + +// MegolmBackupKey is a wrapper around an ECDH X25519 private key that is used +// to decrypt a megolm key backup. +type MegolmBackupKey struct { + *ecdh.PrivateKey +} + +func NewMegolmBackupKey() (*MegolmBackupKey, error) { + key, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + return &MegolmBackupKey{key}, nil +} + +func MegolmBackupKeyFromBytes(bytes []byte) (*MegolmBackupKey, error) { + key, err := ecdh.X25519().NewPrivateKey(bytes) + if err != nil { + return nil, err + } + return &MegolmBackupKey{key}, nil +} diff --git a/vendor/maunium.net/go/mautrix/crypto/canonicaljson/README.md b/vendor/maunium.net/go/mautrix/crypto/canonicaljson/README.md new file mode 100644 index 00000000..da9d71ff --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/canonicaljson/README.md @@ -0,0 +1,6 @@ +# canonicaljson +This is a Go package to produce Matrix [Canonical JSON](https://matrix.org/docs/spec/appendices#canonical-json). +It is essentially just [json.go](https://github.com/matrix-org/gomatrixserverlib/blob/master/json.go) +from gomatrixserverlib without all the other files that are completely useless for non-server use cases. + +The original project is licensed under the Apache 2.0 license. diff --git a/vendor/maunium.net/go/mautrix/crypto/canonicaljson/json.go b/vendor/maunium.net/go/mautrix/crypto/canonicaljson/json.go new file mode 100644 index 00000000..fd296e65 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/canonicaljson/json.go @@ -0,0 +1,257 @@ +/* Copyright 2016-2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package canonicaljson + +import ( + "encoding/binary" + "fmt" + "sort" + "unicode/utf8" + + "github.com/tidwall/gjson" +) + +// CanonicalJSON re-encodes the JSON in a canonical encoding. The encoding is +// the shortest possible encoding using integer values with sorted object keys. +// https://matrix.org/docs/spec/appendices#canonical-json +func CanonicalJSON(input []byte) ([]byte, error) { + if !gjson.Valid(string(input)) { + return nil, fmt.Errorf("invalid json") + } + + return CanonicalJSONAssumeValid(input), nil +} + +// CanonicalJSONAssumeValid is the same as CanonicalJSON, but assumes the +// input is valid JSON +func CanonicalJSONAssumeValid(input []byte) []byte { + input = CompactJSON(input, make([]byte, 0, len(input))) + return SortJSON(input, make([]byte, 0, len(input))) +} + +// SortJSON reencodes the JSON with the object keys sorted by lexicographically +// by codepoint. The input must be valid JSON. +func SortJSON(input, output []byte) []byte { + result := gjson.ParseBytes(input) + + return sortJSONValue(result, input, output) +} + +// sortJSONValue takes a gjson.Result and sorts it. inputJSON must be the +// raw JSON bytes that gjson.Result points to. +func sortJSONValue(input gjson.Result, inputJSON, output []byte) []byte { + if input.IsArray() { + return sortJSONArray(input, inputJSON, output) + } + + if input.IsObject() { + return sortJSONObject(input, inputJSON, output) + } + + // If its neither an object nor an array then there is no sub structure + // to sort, so just append the raw bytes. + return append(output, input.Raw...) +} + +// sortJSONArray takes a gjson.Result and sorts it, assuming its an array. +// inputJSON must be the raw JSON bytes that gjson.Result points to. +func sortJSONArray(input gjson.Result, inputJSON, output []byte) []byte { + sep := byte('[') + + // Iterate over each value in the array and sort it. + input.ForEach(func(_, value gjson.Result) bool { + output = append(output, sep) + sep = ',' + output = sortJSONValue(value, inputJSON, output) + return true // keep iterating + }) + + if sep == '[' { + // If sep is still '[' then the array was empty and we never wrote the + // initial '[', so we write it now along with the closing ']'. + output = append(output, '[', ']') + } else { + // Otherwise we end the array by writing a single ']' + output = append(output, ']') + } + return output +} + +// sortJSONObject takes a gjson.Result and sorts it, assuming its an object. +// inputJSON must be the raw JSON bytes that gjson.Result points to. +func sortJSONObject(input gjson.Result, inputJSON, output []byte) []byte { + type entry struct { + key string // The parsed key string + rawKey string // The raw, unparsed key JSON string + value gjson.Result + } + + var entries []entry + + // Iterate over each key/value pair and add it to a slice + // that we can sort + input.ForEach(func(key, value gjson.Result) bool { + entries = append(entries, entry{ + key: key.String(), + rawKey: key.Raw, + value: value, + }) + return true // keep iterating + }) + + // Sort the slice based on the *parsed* key + sort.Slice(entries, func(a, b int) bool { + return entries[a].key < entries[b].key + }) + + sep := byte('{') + + for _, entry := range entries { + output = append(output, sep) + sep = ',' + + // Append the raw unparsed JSON key, *not* the parsed key + output = append(output, entry.rawKey...) + output = append(output, ':') + output = sortJSONValue(entry.value, inputJSON, output) + } + if sep == '{' { + // If sep is still '{' then the object was empty and we never wrote the + // initial '{', so we write it now along with the closing '}'. + output = append(output, '{', '}') + } else { + // Otherwise we end the object by writing a single '}' + output = append(output, '}') + } + return output +} + +// CompactJSON makes the encoded JSON as small as possible by removing +// whitespace and unneeded unicode escapes +func CompactJSON(input, output []byte) []byte { + var i int + for i < len(input) { + c := input[i] + i++ + // The valid whitespace characters are all less than or equal to SPACE 0x20. + // The valid non-white characters are all greater than SPACE 0x20. + // So we can check for whitespace by comparing against SPACE 0x20. + if c <= ' ' { + // Skip over whitespace. + continue + } + // Add the non-whitespace character to the output. + output = append(output, c) + if c == '"' { + // We are inside a string. + for i < len(input) { + c = input[i] + i++ + // Check if this is an escape sequence. + if c == '\\' { + escape := input[i] + i++ + if escape == 'u' { + // If this is a unicode escape then we need to handle it specially + output, i = compactUnicodeEscape(input, output, i) + } else if escape == '/' { + // JSON does not require escaping '/', but allows encoders to escape it as a special case. + // Since the escape isn't required we remove it. + output = append(output, escape) + } else { + // All other permitted escapes are single charater escapes that are already in their shortest form. + output = append(output, '\\', escape) + } + } else { + output = append(output, c) + } + if c == '"' { + break + } + } + } + } + return output +} + +// compactUnicodeEscape unpacks a 4 byte unicode escape starting at index. +// If the escape is a surrogate pair then decode the 6 byte \uXXXX escape +// that follows. Returns the output slice and a new input index. +func compactUnicodeEscape(input, output []byte, index int) ([]byte, int) { + const ( + ESCAPES = "uuuuuuuubtnufruuuuuuuuuuuuuuuuuu" + HEX = "0123456789ABCDEF" + ) + // If there aren't enough bytes to decode the hex escape then return. + if len(input)-index < 4 { + return output, len(input) + } + // Decode the 4 hex digits. + c := readHexDigits(input[index:]) + index += 4 + if c < ' ' { + // If the character is less than SPACE 0x20 then it will need escaping. + escape := ESCAPES[c] + output = append(output, '\\', escape) + if escape == 'u' { + output = append(output, '0', '0', byte('0'+(c>>4)), HEX[c&0xF]) + } + } else if c == '\\' || c == '"' { + // Otherwise the character only needs escaping if it is a QUOTE '"' or BACKSLASH '\\'. + output = append(output, '\\', byte(c)) + } else if c < 0xD800 || c >= 0xE000 { + // If the character isn't a surrogate pair then encoded it directly as UTF-8. + var buffer [4]byte + n := utf8.EncodeRune(buffer[:], rune(c)) + output = append(output, buffer[:n]...) + } else { + // Otherwise the escaped character was the first part of a UTF-16 style surrogate pair. + // The next 6 bytes MUST be a '\uXXXX'. + // If there aren't enough bytes to decode the hex escape then return. + if len(input)-index < 6 { + return output, len(input) + } + // Decode the 4 hex digits from the '\uXXXX'. + surrogate := readHexDigits(input[index+2:]) + index += 6 + // Reconstruct the UCS4 codepoint from the surrogates. + codepoint := 0x10000 + (((c & 0x3FF) << 10) | (surrogate & 0x3FF)) + // Encode the charater as UTF-8. + var buffer [4]byte + n := utf8.EncodeRune(buffer[:], rune(codepoint)) + output = append(output, buffer[:n]...) + } + return output, index +} + +// Read 4 hex digits from the input slice. +// Taken from https://github.com/NegativeMjark/indolentjson-rust/blob/8b959791fe2656a88f189c5d60d153be05fe3deb/src/readhex.rs#L21 +func readHexDigits(input []byte) uint32 { + hex := binary.BigEndian.Uint32(input) + // subtract '0' + hex -= 0x30303030 + // strip the higher bits, maps 'a' => 'A' + hex &= 0x1F1F1F1F + mask := hex & 0x10101010 + // subtract 'A' - 10 - '9' - 9 = 7 from the letters. + hex -= mask >> 1 + hex += mask >> 4 + // collect the nibbles + hex |= hex >> 4 + hex &= 0xFF00FF + hex |= hex >> 8 + return hex & 0xFFFF +} diff --git a/vendor/maunium.net/go/mautrix/crypto/ed25519/ed25519.go b/vendor/maunium.net/go/mautrix/crypto/ed25519/ed25519.go new file mode 100644 index 00000000..327cbb3c --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/ed25519/ed25519.go @@ -0,0 +1,302 @@ +// Copyright 2024 Sumner Evans. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ed25519 implements the Ed25519 signature algorithm. See +// https://ed25519.cr.yp.to/. +// +// This package stores the private key in the NaCl format, which is a different +// format than that used by the [crypto/ed25519] package in the standard +// library. +// +// This picture will help with the rest of the explanation: +// https://blog.mozilla.org/warner/files/2011/11/key-formats.png +// +// The private key in the [crypto/ed25519] package is a 64-byte value where the +// first 32-bytes are the seed and the last 32-bytes are the public key. +// +// The private key in this package is stored in the NaCl format. That is, the +// left 32-bytes are the private scalar A and the right 32-bytes are the right +// half of the SHA512 result. +// +// The contents of this package are mostly copied from the standard library, +// and as such the source code is licensed under the BSD license of the +// standard library implementation. +// +// Other notable changes from the standard library include: +// +// - The Seed function of the standard library is not implemented in this +// package because there is no way to recover the seed after hashing it. +package ed25519 + +import ( + "crypto" + "crypto/ed25519" + cryptorand "crypto/rand" + "crypto/sha512" + "crypto/subtle" + "errors" + "io" + "strconv" + + "filippo.io/edwards25519" +) + +const ( + // PublicKeySize is the size, in bytes, of public keys as used in this package. + PublicKeySize = 32 + // PrivateKeySize is the size, in bytes, of private keys as used in this package. + PrivateKeySize = 64 + // SignatureSize is the size, in bytes, of signatures generated and verified by this package. + SignatureSize = 64 + // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. + SeedSize = 32 +) + +// PublicKey is the type of Ed25519 public keys. +type PublicKey []byte + +// Any methods implemented on PublicKey might need to also be implemented on +// PrivateKey, as the latter embeds the former and will expose its methods. + +// Equal reports whether pub and x have the same value. +func (pub PublicKey) Equal(x crypto.PublicKey) bool { + switch x := x.(type) { + case PublicKey: + return subtle.ConstantTimeCompare(pub, x) == 1 + case ed25519.PublicKey: + return subtle.ConstantTimeCompare(pub, x) == 1 + default: + return false + } +} + +// PrivateKey is the type of Ed25519 private keys. It implements [crypto.Signer]. +type PrivateKey []byte + +// Public returns the [PublicKey] corresponding to priv. +// +// This method differs from the standard library because it calculates the +// public key instead of returning the right half of the private key (which +// contains the public key in the standard library). +func (priv PrivateKey) Public() crypto.PublicKey { + s, err := edwards25519.NewScalar().SetBytesWithClamping(priv[:32]) + if err != nil { + panic("ed25519: internal error: setting scalar failed") + } + return (&edwards25519.Point{}).ScalarBaseMult(s).Bytes() +} + +// Equal reports whether priv and x have the same value. +func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { + // TODO do we have any need to check equality with standard library ed25519 + // private keys? + xx, ok := x.(PrivateKey) + if !ok { + return false + } + return subtle.ConstantTimeCompare(priv, xx) == 1 +} + +// Sign signs the given message with priv. rand is ignored and can be nil. +// +// If opts.HashFunc() is [crypto.SHA512], the pre-hashed variant Ed25519ph is used +// and message is expected to be a SHA-512 hash, otherwise opts.HashFunc() must +// be [crypto.Hash](0) and the message must not be hashed, as Ed25519 performs two +// passes over messages to be signed. +// +// A value of type [Options] can be used as opts, or crypto.Hash(0) or +// crypto.SHA512 directly to select plain Ed25519 or Ed25519ph, respectively. +func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { + hash := opts.HashFunc() + context := "" + if opts, ok := opts.(*Options); ok { + context = opts.Context + } + switch { + case hash == crypto.SHA512: // Ed25519ph + if l := len(message); l != sha512.Size { + return nil, errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa(l)) + } + if l := len(context); l > 255 { + return nil, errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa(l)) + } + signature := make([]byte, SignatureSize) + sign(signature, priv, message, domPrefixPh, context) + return signature, nil + case hash == crypto.Hash(0) && context != "": // Ed25519ctx + if l := len(context); l > 255 { + return nil, errors.New("ed25519: bad Ed25519ctx context length: " + strconv.Itoa(l)) + } + signature := make([]byte, SignatureSize) + sign(signature, priv, message, domPrefixCtx, context) + return signature, nil + case hash == crypto.Hash(0): // Ed25519 + return Sign(priv, message), nil + default: + return nil, errors.New("ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)") + } +} + +// Options can be used with [PrivateKey.Sign] or [VerifyWithOptions] +// to select Ed25519 variants. +type Options struct { + // Hash can be zero for regular Ed25519, or crypto.SHA512 for Ed25519ph. + Hash crypto.Hash + + // Context, if not empty, selects Ed25519ctx or provides the context string + // for Ed25519ph. It can be at most 255 bytes in length. + Context string +} + +// HashFunc returns o.Hash. +func (o *Options) HashFunc() crypto.Hash { return o.Hash } + +// GenerateKey generates a public/private key pair using entropy from rand. +// If rand is nil, [crypto/rand.Reader] will be used. +// +// The output of this function is deterministic, and equivalent to reading +// [SeedSize] bytes from rand, and passing them to [NewKeyFromSeed]. +func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { + if rand == nil { + rand = cryptorand.Reader + } + + seed := make([]byte, SeedSize) + if _, err := io.ReadFull(rand, seed); err != nil { + return nil, nil, err + } + + privateKey := NewKeyFromSeed(seed) + return PublicKey(privateKey.Public().([]byte)), privateKey, nil +} + +// NewKeyFromSeed calculates a private key from a seed. It will panic if +// len(seed) is not [SeedSize]. This function is provided for interoperability +// with RFC 8032. RFC 8032's private keys correspond to seeds in this +// package. +func NewKeyFromSeed(seed []byte) PrivateKey { + // Outline the function body so that the returned key can be stack-allocated. + privateKey := make([]byte, PrivateKeySize) + newKeyFromSeed(privateKey, seed) + return privateKey +} + +func newKeyFromSeed(privateKey, seed []byte) { + if l := len(seed); l != SeedSize { + panic("ed25519: bad seed length: " + strconv.Itoa(l)) + } + + h := sha512.Sum512(seed) + + // Apply clamping to get A in the left half, and leave the right half + // as-is. This gets the private key into the NaCl format. + h[0] &= 248 + h[31] &= 63 + h[31] |= 64 + copy(privateKey, h[:]) +} + +// Sign signs the message with privateKey and returns a signature. It will +// panic if len(privateKey) is not [PrivateKeySize]. +func Sign(privateKey PrivateKey, message []byte) []byte { + // Outline the function body so that the returned signature can be + // stack-allocated. + signature := make([]byte, SignatureSize) + sign(signature, privateKey, message, domPrefixPure, "") + return signature +} + +// Domain separation prefixes used to disambiguate Ed25519/Ed25519ph/Ed25519ctx. +// See RFC 8032, Section 2 and Section 5.1. +const ( + // domPrefixPure is empty for pure Ed25519. + domPrefixPure = "" + // domPrefixPh is dom2(phflag=1) for Ed25519ph. It must be followed by the + // uint8-length prefixed context. + domPrefixPh = "SigEd25519 no Ed25519 collisions\x01" + // domPrefixCtx is dom2(phflag=0) for Ed25519ctx. It must be followed by the + // uint8-length prefixed context. + domPrefixCtx = "SigEd25519 no Ed25519 collisions\x00" +) + +func sign(signature []byte, privateKey PrivateKey, message []byte, domPrefix, context string) { + if l := len(privateKey); l != PrivateKeySize { + panic("ed25519: bad private key length: " + strconv.Itoa(l)) + } + // We have to extract the public key from the private key. + publicKey := privateKey.Public().([]byte) + // The private key is already the hashed value of the seed. + h := privateKey + + s, err := edwards25519.NewScalar().SetBytesWithClamping(h[:32]) + if err != nil { + panic("ed25519: internal error: setting scalar failed") + } + prefix := h[32:] + + mh := sha512.New() + if domPrefix != domPrefixPure { + mh.Write([]byte(domPrefix)) + mh.Write([]byte{byte(len(context))}) + mh.Write([]byte(context)) + } + mh.Write(prefix) + mh.Write(message) + messageDigest := make([]byte, 0, sha512.Size) + messageDigest = mh.Sum(messageDigest) + r, err := edwards25519.NewScalar().SetUniformBytes(messageDigest) + if err != nil { + panic("ed25519: internal error: setting scalar failed") + } + + R := (&edwards25519.Point{}).ScalarBaseMult(r) + + kh := sha512.New() + if domPrefix != domPrefixPure { + kh.Write([]byte(domPrefix)) + kh.Write([]byte{byte(len(context))}) + kh.Write([]byte(context)) + } + kh.Write(R.Bytes()) + kh.Write(publicKey) + kh.Write(message) + hramDigest := make([]byte, 0, sha512.Size) + hramDigest = kh.Sum(hramDigest) + k, err := edwards25519.NewScalar().SetUniformBytes(hramDigest) + if err != nil { + panic("ed25519: internal error: setting scalar failed") + } + + S := edwards25519.NewScalar().MultiplyAdd(k, s, r) + + copy(signature[:32], R.Bytes()) + copy(signature[32:], S.Bytes()) +} + +// Verify reports whether sig is a valid signature of message by publicKey. It +// will panic if len(publicKey) is not [PublicKeySize]. +// +// This is just a wrapper around [ed25519.Verify] from the standard library. +func Verify(publicKey PublicKey, message, sig []byte) bool { + return ed25519.Verify(ed25519.PublicKey(publicKey), message, sig) +} + +// VerifyWithOptions reports whether sig is a valid signature of message by +// publicKey. A valid signature is indicated by returning a nil error. It will +// panic if len(publicKey) is not [PublicKeySize]. +// +// If opts.Hash is [crypto.SHA512], the pre-hashed variant Ed25519ph is used and +// message is expected to be a SHA-512 hash, otherwise opts.Hash must be +// [crypto.Hash](0) and the message must not be hashed, as Ed25519 performs two +// passes over messages to be signed. +// +// This is just a wrapper around [ed25519.VerifyWithOptions] from the standard +// library. +func VerifyWithOptions(publicKey PublicKey, message, sig []byte, opts *Options) error { + return ed25519.VerifyWithOptions(ed25519.PublicKey(publicKey), message, sig, &ed25519.Options{ + Hash: opts.Hash, + Context: opts.Context, + }) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/aessha2/aessha2.go b/vendor/maunium.net/go/mautrix/crypto/goolm/aessha2/aessha2.go new file mode 100644 index 00000000..42d9811b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/aessha2/aessha2.go @@ -0,0 +1,59 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package aessha2 implements the m.megolm.v1.aes-sha2 encryption algorithm +// described in [Section 10.12.4.3] in the Spec +// +// [Section 10.12.4.3]: https://spec.matrix.org/v1.12/client-server-api/#mmegolmv1aes-sha2 +package aessha2 + +import ( + "crypto/hmac" + "crypto/sha256" + "crypto/subtle" + "io" + + "golang.org/x/crypto/hkdf" + + "maunium.net/go/mautrix/crypto/aescbc" +) + +type AESSHA2 struct { + aesKey, hmacKey, iv []byte +} + +func NewAESSHA2(secret, info []byte) (AESSHA2, error) { + kdf := hkdf.New(sha256.New, secret, nil, info) + keymatter := make([]byte, 80) + _, err := io.ReadFull(kdf, keymatter) + return AESSHA2{ + keymatter[:32], // AES Key + keymatter[32:64], // HMAC Key + keymatter[64:], // IV + }, err +} + +func (a *AESSHA2) Encrypt(plaintext []byte) ([]byte, error) { + return aescbc.Encrypt(a.aesKey, a.iv, plaintext) +} + +func (a *AESSHA2) Decrypt(ciphertext []byte) ([]byte, error) { + return aescbc.Decrypt(a.aesKey, a.iv, ciphertext) +} + +func (a *AESSHA2) MAC(ciphertext []byte) ([]byte, error) { + hash := hmac.New(sha256.New, a.hmacKey) + _, err := hash.Write(ciphertext) + return hash.Sum(nil), err +} + +func (a *AESSHA2) VerifyMAC(ciphertext, theirMAC []byte) (bool, error) { + if mac, err := a.MAC(ciphertext); err != nil { + return false, err + } else { + return subtle.ConstantTimeCompare(mac[:len(theirMAC)], theirMAC) == 1, nil + } +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/curve25519.go b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/curve25519.go new file mode 100644 index 00000000..e9759501 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/curve25519.go @@ -0,0 +1,126 @@ +package crypto + +import ( + "crypto/rand" + "crypto/subtle" + "encoding/base64" + + "golang.org/x/crypto/curve25519" + + "maunium.net/go/mautrix/crypto/goolm/libolmpickle" + "maunium.net/go/mautrix/id" +) + +const ( + Curve25519PrivateKeyLength = curve25519.ScalarSize //The length of the private key. + Curve25519PublicKeyLength = 32 +) + +// Curve25519KeyPair stores both parts of a curve25519 key. +type Curve25519KeyPair struct { + PrivateKey Curve25519PrivateKey `json:"private,omitempty"` + PublicKey Curve25519PublicKey `json:"public,omitempty"` +} + +// Curve25519GenerateKey creates a new curve25519 key pair. +func Curve25519GenerateKey() (Curve25519KeyPair, error) { + privateKeyByte := make([]byte, Curve25519PrivateKeyLength) + if _, err := rand.Read(privateKeyByte); err != nil { + return Curve25519KeyPair{}, err + } + + privateKey := Curve25519PrivateKey(privateKeyByte) + publicKey, err := privateKey.PubKey() + return Curve25519KeyPair{ + PrivateKey: Curve25519PrivateKey(privateKey), + PublicKey: Curve25519PublicKey(publicKey), + }, err +} + +// Curve25519GenerateFromPrivate creates a new curve25519 key pair with the private key given. +func Curve25519GenerateFromPrivate(private Curve25519PrivateKey) (Curve25519KeyPair, error) { + publicKey, err := private.PubKey() + return Curve25519KeyPair{ + PrivateKey: private, + PublicKey: Curve25519PublicKey(publicKey), + }, err +} + +// B64Encoded returns a base64 encoded string of the public key. +func (c Curve25519KeyPair) B64Encoded() id.Curve25519 { + return c.PublicKey.B64Encoded() +} + +// SharedSecret returns the shared secret between the key pair and the given public key. +func (c Curve25519KeyPair) SharedSecret(pubKey Curve25519PublicKey) ([]byte, error) { + return c.PrivateKey.SharedSecret(pubKey) +} + +// PickleLibOlm pickles the key pair into the encoder. +func (c Curve25519KeyPair) PickleLibOlm(encoder *libolmpickle.Encoder) { + c.PublicKey.PickleLibOlm(encoder) + if len(c.PrivateKey) == Curve25519PrivateKeyLength { + encoder.Write(c.PrivateKey) + } else { + encoder.WriteEmptyBytes(Curve25519PrivateKeyLength) + } +} + +// UnpickleLibOlm decodes the unencryted value and populates the key pair accordingly. It returns the number of bytes read. +func (c *Curve25519KeyPair) UnpickleLibOlm(decoder *libolmpickle.Decoder) error { + if err := c.PublicKey.UnpickleLibOlm(decoder); err != nil { + return err + } else if privKey, err := decoder.ReadBytes(Curve25519PrivateKeyLength); err != nil { + return err + } else { + c.PrivateKey = privKey + return nil + } +} + +// Curve25519PrivateKey represents the private key for curve25519 usage +type Curve25519PrivateKey []byte + +// Equal compares the private key to the given private key. +func (c Curve25519PrivateKey) Equal(x Curve25519PrivateKey) bool { + return subtle.ConstantTimeCompare(c, x) == 1 +} + +// PubKey returns the public key derived from the private key. +func (c Curve25519PrivateKey) PubKey() (Curve25519PublicKey, error) { + return curve25519.X25519(c, curve25519.Basepoint) +} + +// SharedSecret returns the shared secret between the private key and the given public key. +func (c Curve25519PrivateKey) SharedSecret(pubKey Curve25519PublicKey) ([]byte, error) { + return curve25519.X25519(c, pubKey) +} + +// Curve25519PublicKey represents the public key for curve25519 usage +type Curve25519PublicKey []byte + +// Equal compares the public key to the given public key. +func (c Curve25519PublicKey) Equal(x Curve25519PublicKey) bool { + return subtle.ConstantTimeCompare(c, x) == 1 +} + +// B64Encoded returns a base64 encoded string of the public key. +func (c Curve25519PublicKey) B64Encoded() id.Curve25519 { + return id.Curve25519(base64.RawStdEncoding.EncodeToString(c)) +} + +// PickleLibOlm pickles the public key into the encoder. +func (c Curve25519PublicKey) PickleLibOlm(encoder *libolmpickle.Encoder) { + if len(c) == Curve25519PublicKeyLength { + encoder.Write(c) + } else { + encoder.WriteEmptyBytes(Curve25519PublicKeyLength) + } +} + +// UnpickleLibOlm decodes the unencryted value and populates the public key accordingly. It returns the number of bytes read. +func (c *Curve25519PublicKey) UnpickleLibOlm(decoder *libolmpickle.Decoder) error { + pubkey, err := decoder.ReadBytes(Curve25519PublicKeyLength) + *c = pubkey + return err +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/doc.go b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/doc.go new file mode 100644 index 00000000..5bdb01d8 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/doc.go @@ -0,0 +1,2 @@ +// Package crpyto provides the nessesary encryption methods for olm/megolm +package crypto diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/ed25519.go b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/ed25519.go new file mode 100644 index 00000000..a3345ba9 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/ed25519.go @@ -0,0 +1,136 @@ +package crypto + +import ( + "encoding/base64" + + "maunium.net/go/mautrix/crypto/ed25519" + "maunium.net/go/mautrix/crypto/goolm/libolmpickle" + "maunium.net/go/mautrix/id" +) + +const ( + Ed25519SignatureSize = ed25519.SignatureSize //The length of a signature +) + +// Ed25519GenerateKey creates a new ed25519 key pair. +func Ed25519GenerateKey() (Ed25519KeyPair, error) { + publicKey, privateKey, err := ed25519.GenerateKey(nil) + return Ed25519KeyPair{ + PrivateKey: Ed25519PrivateKey(privateKey), + PublicKey: Ed25519PublicKey(publicKey), + }, err +} + +// Ed25519GenerateFromPrivate creates a new ed25519 key pair with the private key given. +func Ed25519GenerateFromPrivate(privKey Ed25519PrivateKey) Ed25519KeyPair { + return Ed25519KeyPair{ + PrivateKey: privKey, + PublicKey: privKey.PubKey(), + } +} + +// Ed25519GenerateFromSeed creates a new ed25519 key pair with a given seed. +func Ed25519GenerateFromSeed(seed []byte) Ed25519KeyPair { + privKey := Ed25519PrivateKey(ed25519.NewKeyFromSeed(seed)) + return Ed25519KeyPair{ + PrivateKey: privKey, + PublicKey: privKey.PubKey(), + } +} + +// Ed25519KeyPair stores both parts of a ed25519 key. +type Ed25519KeyPair struct { + PrivateKey Ed25519PrivateKey `json:"private,omitempty"` + PublicKey Ed25519PublicKey `json:"public,omitempty"` +} + +// B64Encoded returns a base64 encoded string of the public key. +func (c Ed25519KeyPair) B64Encoded() id.Ed25519 { + return id.Ed25519(base64.RawStdEncoding.EncodeToString(c.PublicKey)) +} + +// Sign returns the signature for the message. +func (c Ed25519KeyPair) Sign(message []byte) ([]byte, error) { + return c.PrivateKey.Sign(message) +} + +// Verify checks the signature of the message against the givenSignature +func (c Ed25519KeyPair) Verify(message, givenSignature []byte) bool { + return c.PublicKey.Verify(message, givenSignature) +} + +// PickleLibOlm pickles the key pair into the encoder. +func (c Ed25519KeyPair) PickleLibOlm(encoder *libolmpickle.Encoder) { + c.PublicKey.PickleLibOlm(encoder) + if len(c.PrivateKey) == ed25519.PrivateKeySize { + encoder.Write(c.PrivateKey) + } else { + encoder.WriteEmptyBytes(ed25519.PrivateKeySize) + } +} + +// UnpickleLibOlm unpickles the unencryted value and populates the key pair accordingly. +func (c *Ed25519KeyPair) UnpickleLibOlm(decoder *libolmpickle.Decoder) error { + if err := c.PublicKey.UnpickleLibOlm(decoder); err != nil { + return err + } else if privKey, err := decoder.ReadBytes(ed25519.PrivateKeySize); err != nil { + return err + } else { + c.PrivateKey = privKey + return nil + } +} + +// Curve25519PrivateKey represents the private key for ed25519 usage. This is just a wrapper. +type Ed25519PrivateKey ed25519.PrivateKey + +// Equal compares the private key to the given private key. +func (c Ed25519PrivateKey) Equal(x Ed25519PrivateKey) bool { + return ed25519.PrivateKey(c).Equal(ed25519.PrivateKey(x)) +} + +// PubKey returns the public key derived from the private key. +func (c Ed25519PrivateKey) PubKey() Ed25519PublicKey { + publicKey := ed25519.PrivateKey(c).Public() + return Ed25519PublicKey(publicKey.([]byte)) +} + +// Sign returns the signature for the message. +func (c Ed25519PrivateKey) Sign(message []byte) ([]byte, error) { + return ed25519.PrivateKey(c).Sign(nil, message, &ed25519.Options{}) +} + +// Ed25519PublicKey represents the public key for ed25519 usage. This is just a wrapper. +type Ed25519PublicKey ed25519.PublicKey + +// Equal compares the public key to the given public key. +func (c Ed25519PublicKey) Equal(x Ed25519PublicKey) bool { + return ed25519.PublicKey(c).Equal(ed25519.PublicKey(x)) +} + +// B64Encoded returns a base64 encoded string of the public key. +func (c Ed25519PublicKey) B64Encoded() id.Curve25519 { + return id.Curve25519(base64.RawStdEncoding.EncodeToString(c)) +} + +// Verify checks the signature of the message against the givenSignature +func (c Ed25519PublicKey) Verify(message, givenSignature []byte) bool { + return ed25519.Verify(ed25519.PublicKey(c), message, givenSignature) +} + +// PickleLibOlm pickles the public key into the encoder. +func (c Ed25519PublicKey) PickleLibOlm(encoder *libolmpickle.Encoder) { + if len(c) == ed25519.PublicKeySize { + encoder.Write(c) + } else { + encoder.WriteEmptyBytes(ed25519.PublicKeySize) + } +} + +// UnpickleLibOlm unpickles the unencryted value and populates the public key +// accordingly. +func (c *Ed25519PublicKey) UnpickleLibOlm(decoder *libolmpickle.Decoder) error { + key, err := decoder.ReadBytes(ed25519.PublicKeySize) + *c = key + return err +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/one_time_key.go b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/one_time_key.go new file mode 100644 index 00000000..888b1749 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/crypto/one_time_key.go @@ -0,0 +1,46 @@ +package crypto + +import ( + "encoding/base64" + "encoding/binary" + + "maunium.net/go/mautrix/crypto/goolm/libolmpickle" +) + +// OneTimeKey stores the information about a one time key. +type OneTimeKey struct { + ID uint32 `json:"id"` + Published bool `json:"published"` + Key Curve25519KeyPair `json:"key,omitempty"` +} + +// Equal compares the one time key to the given one. +func (otk OneTimeKey) Equal(other OneTimeKey) bool { + return otk.ID == other.ID && + otk.Published == other.Published && + otk.Key.PrivateKey.Equal(other.Key.PrivateKey) && + otk.Key.PublicKey.Equal(other.Key.PublicKey) +} + +// PickleLibOlm pickles the key pair into the encoder. +func (c OneTimeKey) PickleLibOlm(encoder *libolmpickle.Encoder) { + encoder.WriteUInt32(c.ID) + encoder.WriteBool(c.Published) + c.Key.PickleLibOlm(encoder) +} + +// UnpickleLibOlm unpickles the unencryted value and populates the [OneTimeKey] +// accordingly. +func (c *OneTimeKey) UnpickleLibOlm(decoder *libolmpickle.Decoder) (err error) { + if c.ID, err = decoder.ReadUInt32(); err != nil { + return + } else if c.Published, err = decoder.ReadBool(); err != nil { + return + } + return c.Key.UnpickleLibOlm(decoder) +} + +// KeyIDEncoded returns the base64 encoded key ID. +func (c OneTimeKey) KeyIDEncoded() string { + return base64.RawStdEncoding.EncodeToString(binary.BigEndian.AppendUint32(nil, c.ID)) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go b/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go new file mode 100644 index 00000000..061a052a --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/goolmbase64/base64.go @@ -0,0 +1,22 @@ +package goolmbase64 + +import ( + "encoding/base64" +) + +// Deprecated: base64.RawStdEncoding should be used directly +func Decode(input []byte) ([]byte, error) { + decoded := make([]byte, base64.RawStdEncoding.DecodedLen(len(input))) + writtenBytes, err := base64.RawStdEncoding.Decode(decoded, input) + if err != nil { + return nil, err + } + return decoded[:writtenBytes], nil +} + +// Deprecated: base64.RawStdEncoding should be used directly +func Encode(input []byte) []byte { + encoded := make([]byte, base64.RawStdEncoding.EncodedLen(len(input))) + base64.RawStdEncoding.Encode(encoded, input) + return encoded +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/encoder.go b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/encoder.go new file mode 100644 index 00000000..63e7b09b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/encoder.go @@ -0,0 +1,40 @@ +package libolmpickle + +import ( + "bytes" + "encoding/binary" + + "go.mau.fi/util/exerrors" +) + +const ( + PickleBoolLength = 1 + PickleUInt8Length = 1 + PickleUInt32Length = 4 +) + +type Encoder struct { + bytes.Buffer +} + +func NewEncoder() *Encoder { return &Encoder{} } + +func (p *Encoder) WriteUInt8(value uint8) { + exerrors.PanicIfNotNil(p.WriteByte(value)) +} + +func (p *Encoder) WriteBool(value bool) { + if value { + exerrors.PanicIfNotNil(p.WriteByte(0x01)) + } else { + exerrors.PanicIfNotNil(p.WriteByte(0x00)) + } +} + +func (p *Encoder) WriteEmptyBytes(count int) { + exerrors.Must(p.Write(make([]byte, count))) +} + +func (p *Encoder) WriteUInt32(value uint32) { + exerrors.PanicIfNotNil(binary.Write(&p.Buffer, binary.BigEndian, value)) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/pickle.go b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/pickle.go new file mode 100644 index 00000000..d15358fd --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/pickle.go @@ -0,0 +1,48 @@ +package libolmpickle + +import ( + "crypto/aes" + "fmt" + + "maunium.net/go/mautrix/crypto/goolm/aessha2" + "maunium.net/go/mautrix/crypto/goolm/goolmbase64" + "maunium.net/go/mautrix/crypto/olm" +) + +const pickleMACLength = 8 + +var kdfPickle = []byte("Pickle") //used to derive the keys for encryption + +// Pickle encrypts the input with the key and the cipher AESSHA256. The result is then encoded in base64. +func Pickle(key, plaintext []byte) ([]byte, error) { + if c, err := aessha2.NewAESSHA2(key, kdfPickle); err != nil { + return nil, err + } else if ciphertext, err := c.Encrypt(plaintext); err != nil { + return nil, err + } else if mac, err := c.MAC(ciphertext); err != nil { + return nil, err + } else { + return goolmbase64.Encode(append(ciphertext, mac[:pickleMACLength]...)), nil + } +} + +// Unpickle decodes the input from base64 and decrypts the decoded input with the key and the cipher AESSHA256. +func Unpickle(key, input []byte) ([]byte, error) { + ciphertext, err := goolmbase64.Decode(input) + if err != nil { + return nil, err + } + ciphertext, mac := ciphertext[:len(ciphertext)-pickleMACLength], ciphertext[len(ciphertext)-pickleMACLength:] + if c, err := aessha2.NewAESSHA2(key, kdfPickle); err != nil { + return nil, err + } else if verified, err := c.VerifyMAC(ciphertext, mac); err != nil { + return nil, err + } else if !verified { + return nil, fmt.Errorf("decrypt pickle: %w", olm.ErrBadMAC) + } else { + // Set to next block size + targetCipherText := make([]byte, int(len(ciphertext)/aes.BlockSize)*aes.BlockSize) + copy(targetCipherText, ciphertext) + return c.Decrypt(targetCipherText) + } +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go new file mode 100644 index 00000000..308e472c --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/picklejson.go @@ -0,0 +1,60 @@ +package libolmpickle + +import ( + "crypto/aes" + "encoding/json" + "fmt" + + "maunium.net/go/mautrix/crypto/olm" +) + +// PickleAsJSON returns an object as a base64 string encrypted using the supplied key. The unencrypted representation of the object is in JSON format. +func PickleAsJSON(object any, pickleVersion byte, key []byte) ([]byte, error) { + if len(key) == 0 { + return nil, fmt.Errorf("pickle: %w", olm.ErrNoKeyProvided) + } + marshaled, err := json.Marshal(object) + if err != nil { + return nil, fmt.Errorf("pickle marshal: %w", err) + } + marshaled = append([]byte{pickleVersion}, marshaled...) + toEncrypt := make([]byte, len(marshaled)) + copy(toEncrypt, marshaled) + //pad marshaled to get block size + if len(marshaled)%aes.BlockSize != 0 { + padding := aes.BlockSize - len(marshaled)%aes.BlockSize + toEncrypt = make([]byte, len(marshaled)+padding) + copy(toEncrypt, marshaled) + } + encrypted, err := Pickle(key, toEncrypt) + if err != nil { + return nil, fmt.Errorf("pickle encrypt: %w", err) + } + return encrypted, nil +} + +// UnpickleAsJSON updates the object by a base64 encrypted string using the supplied key. The unencrypted representation has to be in JSON format. +func UnpickleAsJSON(object any, pickled, key []byte, pickleVersion byte) error { + if len(key) == 0 { + return fmt.Errorf("unpickle: %w", olm.ErrNoKeyProvided) + } + decrypted, err := Unpickle(key, pickled) + if err != nil { + return fmt.Errorf("unpickle decrypt: %w", err) + } + //unpad decrypted so unmarshal works + for i := len(decrypted) - 1; i >= 0; i-- { + if decrypted[i] != 0 { + decrypted = decrypted[:i+1] + break + } + } + if decrypted[0] != pickleVersion { + return fmt.Errorf("unpickle: %w", olm.ErrWrongPickleVersion) + } + err = json.Unmarshal(decrypted[1:], object) + if err != nil { + return fmt.Errorf("unpickle unmarshal: %w", err) + } + return nil +} diff --git a/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/unpickle.go b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/unpickle.go new file mode 100644 index 00000000..d13be315 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/goolm/libolmpickle/unpickle.go @@ -0,0 +1,52 @@ +package libolmpickle + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +func isZeroByteSlice(data []byte) bool { + for _, b := range data { + if b != 0 { + return false + } + } + return true +} + +type Decoder struct { + buf bytes.Buffer +} + +func NewDecoder(buf []byte) *Decoder { + return &Decoder{buf: *bytes.NewBuffer(buf)} +} + +func (d *Decoder) ReadUInt8() (uint8, error) { + return d.buf.ReadByte() +} + +func (d *Decoder) ReadBool() (bool, error) { + val, err := d.buf.ReadByte() + return val != 0x00, err +} + +func (d *Decoder) ReadBytes(length int) (data []byte, err error) { + data = d.buf.Next(length) + if len(data) != length { + return nil, fmt.Errorf("only %d in buffer, expected %d", len(data), length) + } else if isZeroByteSlice(data) { + return nil, nil + } + return +} + +func (d *Decoder) ReadUInt32() (uint32, error) { + data := d.buf.Next(4) + if len(data) != 4 { + return 0, fmt.Errorf("only %d bytes is buffer, expected 4 for uint32", len(data)) + } else { + return binary.BigEndian.Uint32(data), nil + } +} diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/README.md b/vendor/maunium.net/go/mautrix/crypto/olm/README.md new file mode 100644 index 00000000..7d8086c0 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/README.md @@ -0,0 +1,4 @@ +# Go olm bindings +Based on [Dhole/go-olm](https://github.com/Dhole/go-olm) + +The original project is licensed under the Apache 2.0 license. diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/account.go b/vendor/maunium.net/go/mautrix/crypto/olm/account.go new file mode 100644 index 00000000..2ec5dd70 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/account.go @@ -0,0 +1,112 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import ( + "maunium.net/go/mautrix/id" +) + +type Account interface { + // Pickle returns an Account as a base64 string. Encrypts the Account using the + // supplied key. + Pickle(key []byte) ([]byte, error) + + // Unpickle loads an Account from a pickled base64 string. Decrypts the + // Account using the supplied key. Returns error on failure. + Unpickle(pickled, key []byte) error + + // IdentityKeysJSON returns the public parts of the identity keys for the Account. + IdentityKeysJSON() ([]byte, error) + + // IdentityKeys returns the public parts of the Ed25519 and Curve25519 identity + // keys for the Account. + IdentityKeys() (id.Ed25519, id.Curve25519, error) + + // Sign returns the signature of a message using the ed25519 key for this + // Account. + Sign(message []byte) ([]byte, error) + + // OneTimeKeys returns the public parts of the unpublished one time keys for + // the Account. + // + // The returned data is a struct with the single value "Curve25519", which is + // itself an object mapping key id to base64-encoded Curve25519 key. For + // example: + // + // { + // Curve25519: { + // "AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo", + // "AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU" + // } + // } + OneTimeKeys() (map[string]id.Curve25519, error) + + // MarkKeysAsPublished marks the current set of one time keys as being + // published. + MarkKeysAsPublished() + + // MaxNumberOfOneTimeKeys returns the largest number of one time keys this + // Account can store. + MaxNumberOfOneTimeKeys() uint + + // GenOneTimeKeys generates a number of new one time keys. If the total + // number of keys stored by this Account exceeds MaxNumberOfOneTimeKeys + // then the old keys are discarded. + GenOneTimeKeys(num uint) error + + // NewOutboundSession creates a new out-bound session for sending messages to a + // given curve25519 identityKey and oneTimeKey. Returns error on failure. If the + // keys couldn't be decoded as base64 then the error will be "INVALID_BASE64" + NewOutboundSession(theirIdentityKey, theirOneTimeKey id.Curve25519) (Session, error) + + // NewInboundSession creates a new in-bound session for sending/receiving + // messages from an incoming PRE_KEY message. Returns error on failure. If + // the base64 couldn't be decoded then the error will be "INVALID_BASE64". If + // the message was for an unsupported protocol version then the error will be + // "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then the + // error will be "BAD_MESSAGE_FORMAT". If the message refers to an unknown one + // time key then the error will be "BAD_MESSAGE_KEY_ID". + NewInboundSession(oneTimeKeyMsg string) (Session, error) + + // NewInboundSessionFrom creates a new in-bound session for sending/receiving + // messages from an incoming PRE_KEY message. Returns error on failure. If + // the base64 couldn't be decoded then the error will be "INVALID_BASE64". If + // the message was for an unsupported protocol version then the error will be + // "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then the + // error will be "BAD_MESSAGE_FORMAT". If the message refers to an unknown one + // time key then the error will be "BAD_MESSAGE_KEY_ID". + NewInboundSessionFrom(theirIdentityKey *id.Curve25519, oneTimeKeyMsg string) (Session, error) + + // RemoveOneTimeKeys removes the one time keys that the session used from the + // Account. Returns error on failure. If the Account doesn't have any + // matching one time keys then the error will be "BAD_MESSAGE_KEY_ID". + RemoveOneTimeKeys(s Session) error +} + +var Driver = "none" + +var InitBlankAccount func() Account +var InitNewAccount func() (Account, error) +var InitNewAccountFromPickled func(pickled, key []byte) (Account, error) + +// NewAccount creates a new Account. +func NewAccount() (Account, error) { + return InitNewAccount() +} + +func NewBlankAccount() Account { + return InitBlankAccount() +} + +// AccountFromPickled loads an Account from a pickled base64 string. Decrypts +// the Account using the supplied key. Returns error on failure. If the key +// doesn't match the one used to encrypt the Account then the error will be +// "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then the error will be +// "INVALID_BASE64". +func AccountFromPickled(pickled, key []byte) (Account, error) { + return InitNewAccountFromPickled(pickled, key) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/errors.go b/vendor/maunium.net/go/mautrix/crypto/olm/errors.go new file mode 100644 index 00000000..957d7928 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/errors.go @@ -0,0 +1,59 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import "errors" + +// Those are the most common used errors +var ( + ErrBadSignature = errors.New("bad signature") + ErrBadMAC = errors.New("bad mac") + ErrBadMessageFormat = errors.New("bad message format") + ErrBadVerification = errors.New("bad verification") + ErrWrongProtocolVersion = errors.New("wrong protocol version") + ErrEmptyInput = errors.New("empty input") + ErrNoKeyProvided = errors.New("no key") + ErrBadMessageKeyID = errors.New("bad message key id") + ErrRatchetNotAvailable = errors.New("ratchet not available: attempt to decode a message whose index is earlier than our earliest known session key") + ErrMsgIndexTooHigh = errors.New("message index too high") + ErrProtocolViolation = errors.New("not protocol message order") + ErrMessageKeyNotFound = errors.New("message key not found") + ErrChainTooHigh = errors.New("chain index too high") + ErrBadInput = errors.New("bad input") + ErrBadVersion = errors.New("wrong version") + ErrWrongPickleVersion = errors.New("wrong pickle version") + ErrInputToSmall = errors.New("input too small (truncated?)") + ErrOverflow = errors.New("overflow") +) + +// Error codes from go-olm +var ( + EmptyInput = errors.New("empty input") + NoKeyProvided = errors.New("no pickle key provided") + NotEnoughGoRandom = errors.New("couldn't get enough randomness from crypto/rand") + SignatureNotFound = errors.New("input JSON doesn't contain signature from specified device") + InputNotJSONString = errors.New("input doesn't look like a JSON string") +) + +// Error codes from olm code +var ( + NotEnoughRandom = errors.New("not enough entropy was supplied") + OutputBufferTooSmall = errors.New("supplied output buffer is too small") + BadMessageVersion = errors.New("the message version is unsupported") + BadMessageFormat = errors.New("the message couldn't be decoded") + BadMessageMAC = errors.New("the message couldn't be decrypted") + BadMessageKeyID = errors.New("the message references an unknown key ID") + InvalidBase64 = errors.New("the input base64 was invalid") + BadAccountKey = errors.New("the supplied account key is invalid") + UnknownPickleVersion = errors.New("the pickled object is too new") + CorruptedPickle = errors.New("the pickled object couldn't be decoded") + BadSessionKey = errors.New("attempt to initialise an inbound group session from an invalid session key") + UnknownMessageIndex = errors.New("attempt to decode a message whose index is earlier than our earliest known session key") + BadLegacyAccountPickle = errors.New("attempt to unpickle an account which uses pickle version 1") + BadSignature = errors.New("received message had a bad signature") + InputBufferTooSmall = errors.New("the input data was too small to be valid") +) diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/inboundgroupsession.go b/vendor/maunium.net/go/mautrix/crypto/olm/inboundgroupsession.go new file mode 100644 index 00000000..8839b48c --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/inboundgroupsession.go @@ -0,0 +1,80 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import "maunium.net/go/mautrix/id" + +type InboundGroupSession interface { + // Pickle returns an InboundGroupSession as a base64 string. Encrypts the + // InboundGroupSession using the supplied key. + Pickle(key []byte) ([]byte, error) + + // Unpickle loads an [InboundGroupSession] from a pickled base64 string. + // Decrypts the [InboundGroupSession] using the supplied key. + Unpickle(pickled, key []byte) error + + // Decrypt decrypts a message using the [InboundGroupSession]. Returns the + // plain-text and message index on success. Returns error on failure. If + // the base64 couldn't be decoded then the error will be "INVALID_BASE64". + // If the message is for an unsupported version of the protocol then the + // error will be "BAD_MESSAGE_VERSION". If the message couldn't be decoded + // then the error will be BAD_MESSAGE_FORMAT". If the MAC on the message + // was invalid then the error will be "BAD_MESSAGE_MAC". If we do not have + // a session key corresponding to the message's index (ie, it was sent + // before the session key was shared with us) the error will be + // "OLM_UNKNOWN_MESSAGE_INDEX". + Decrypt(message []byte) ([]byte, uint, error) + + // ID returns a base64-encoded identifier for this session. + ID() id.SessionID + + // FirstKnownIndex returns the first message index we know how to decrypt. + FirstKnownIndex() uint32 + + // IsVerified check if the session has been verified as a valid session. + // (A session is verified either because the original session share was + // signed, or because we have subsequently successfully decrypted a + // message.) + IsVerified() bool + + // Export returns the base64-encoded ratchet key for this session, at the + // given index, in a format which can be used by + // InboundGroupSession.InboundGroupSessionImport(). Encrypts the + // InboundGroupSession using the supplied key. Returns error on failure. + // if we do not have a session key corresponding to the given index (ie, it + // was sent before the session key was shared with us) the error will be + // "OLM_UNKNOWN_MESSAGE_INDEX". + Export(messageIndex uint32) ([]byte, error) +} + +var InitInboundGroupSessionFromPickled func(pickled, key []byte) (InboundGroupSession, error) +var InitNewInboundGroupSession func(sessionKey []byte) (InboundGroupSession, error) +var InitInboundGroupSessionImport func(sessionKey []byte) (InboundGroupSession, error) +var InitBlankInboundGroupSession func() InboundGroupSession + +// InboundGroupSessionFromPickled loads an InboundGroupSession from a pickled +// base64 string. Decrypts the InboundGroupSession using the supplied key. +// Returns error on failure. +func InboundGroupSessionFromPickled(pickled, key []byte) (InboundGroupSession, error) { + return InitInboundGroupSessionFromPickled(pickled, key) +} + +// NewInboundGroupSession creates a new inbound group session from a key +// exported from OutboundGroupSession.Key(). Returns error on failure. +func NewInboundGroupSession(sessionKey []byte) (InboundGroupSession, error) { + return InitNewInboundGroupSession(sessionKey) +} + +// InboundGroupSessionImport imports an inbound group session from a previous +// export. Returns error on failure. +func InboundGroupSessionImport(sessionKey []byte) (InboundGroupSession, error) { + return InitInboundGroupSessionImport(sessionKey) +} + +func NewBlankInboundGroupSession() InboundGroupSession { + return InitBlankInboundGroupSession() +} diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/olm.go b/vendor/maunium.net/go/mautrix/crypto/olm/olm.go new file mode 100644 index 00000000..fa2345e1 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/olm.go @@ -0,0 +1,20 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +var GetVersion func() (major, minor, patch uint8) +var SetPickleKeyImpl func(key []byte) + +// Version returns the version number of the olm library. +func Version() (major, minor, patch uint8) { + return GetVersion() +} + +// SetPickleKey sets the global pickle key used when encoding structs with Gob or JSON. +func SetPickleKey(key []byte) { + SetPickleKeyImpl(key) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/outboundgroupsession.go b/vendor/maunium.net/go/mautrix/crypto/olm/outboundgroupsession.go new file mode 100644 index 00000000..7e582b7e --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/outboundgroupsession.go @@ -0,0 +1,57 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import "maunium.net/go/mautrix/id" + +type OutboundGroupSession interface { + // Pickle returns a Session as a base64 string. Encrypts the Session using + // the supplied key. + Pickle(key []byte) ([]byte, error) + + // Unpickle loads an [OutboundGroupSession] from a pickled base64 string. + // Decrypts the [OutboundGroupSession] using the supplied key. + Unpickle(pickled, key []byte) error + + // Encrypt encrypts a message using the [OutboundGroupSession]. Returns the + // encrypted message as base64. + Encrypt(plaintext []byte) ([]byte, error) + + // ID returns a base64-encoded identifier for this session. + ID() id.SessionID + + // MessageIndex returns the message index for this session. Each message + // is sent with an increasing index; this returns the index for the next + // message. + MessageIndex() uint + + // Key returns the base64-encoded current ratchet key for this session. + Key() string +} + +var InitNewOutboundGroupSessionFromPickled func(pickled, key []byte) (OutboundGroupSession, error) +var InitNewOutboundGroupSession func() (OutboundGroupSession, error) +var InitNewBlankOutboundGroupSession func() OutboundGroupSession + +// OutboundGroupSessionFromPickled loads an OutboundGroupSession from a pickled +// base64 string. Decrypts the OutboundGroupSession using the supplied key. +// Returns error on failure. If the key doesn't match the one used to encrypt +// the OutboundGroupSession then the error will be "BAD_SESSION_KEY". If the +// base64 couldn't be decoded then the error will be "INVALID_BASE64". +func OutboundGroupSessionFromPickled(pickled, key []byte) (OutboundGroupSession, error) { + return InitNewOutboundGroupSessionFromPickled(pickled, key) +} + +// NewOutboundGroupSession creates a new outbound group session. +func NewOutboundGroupSession() (OutboundGroupSession, error) { + return InitNewOutboundGroupSession() +} + +// NewBlankOutboundGroupSession initialises an empty [OutboundGroupSession]. +func NewBlankOutboundGroupSession() OutboundGroupSession { + return InitNewBlankOutboundGroupSession() +} diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/pk.go b/vendor/maunium.net/go/mautrix/crypto/olm/pk.go new file mode 100644 index 00000000..70ee452d --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/pk.go @@ -0,0 +1,57 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import ( + "maunium.net/go/mautrix/id" +) + +// PKSigning is an interface for signing messages. +type PKSigning interface { + // Seed returns the seed of the key. + Seed() []byte + + // PublicKey returns the public key. + PublicKey() id.Ed25519 + + // Sign creates a signature for the given message using this key. + Sign(message []byte) ([]byte, error) + + // SignJSON creates a signature for the given object after encoding it to + // canonical JSON. + SignJSON(obj any) (string, error) +} + +// PKDecryption is an interface for decrypting messages. +type PKDecryption interface { + // PublicKey returns the public key. + PublicKey() id.Curve25519 + + // Decrypt verifies and decrypts the given message. + Decrypt(ephemeralKey, mac, ciphertext []byte) ([]byte, error) +} + +var InitNewPKSigning func() (PKSigning, error) +var InitNewPKSigningFromSeed func(seed []byte) (PKSigning, error) +var InitNewPKDecryptionFromPrivateKey func(privateKey []byte) (PKDecryption, error) + +// NewPKSigning creates a new [PKSigning] object, containing a key pair for +// signing messages. +func NewPKSigning() (PKSigning, error) { + return InitNewPKSigning() +} + +// NewPKSigningFromSeed creates a new PKSigning object using the given seed. +func NewPKSigningFromSeed(seed []byte) (PKSigning, error) { + return InitNewPKSigningFromSeed(seed) +} + +// NewPKDecryptionFromPrivateKey creates a new [PKDecryption] from a +// base64-encoded private key. +func NewPKDecryptionFromPrivateKey(privateKey []byte) (PKDecryption, error) { + return InitNewPKDecryptionFromPrivateKey(privateKey) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/olm/session.go b/vendor/maunium.net/go/mautrix/crypto/olm/session.go new file mode 100644 index 00000000..c4b91ffc --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/olm/session.go @@ -0,0 +1,83 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import "maunium.net/go/mautrix/id" + +type Session interface { + // Pickle returns a Session as a base64 string. Encrypts the Session using + // the supplied key. + Pickle(key []byte) ([]byte, error) + + // Unpickle loads a Session from a pickled base64 string. Decrypts the + // Session using the supplied key. + Unpickle(pickled, key []byte) error + + // ID returns an identifier for this Session. Will be the same for both + // ends of the conversation. + ID() id.SessionID + + // HasReceivedMessage returns true if this session has received any + // message. + HasReceivedMessage() bool + + // MatchesInboundSession checks if the PRE_KEY message is for this in-bound + // Session. This can happen if multiple messages are sent to this Account + // before this Account sends a message in reply. Returns true if the + // session matches. Returns false if the session does not match. Returns + // error on failure. If the base64 couldn't be decoded then the error will + // be "INVALID_BASE64". If the message was for an unsupported protocol + // version then the error will be "BAD_MESSAGE_VERSION". If the message + // couldn't be decoded then then the error will be "BAD_MESSAGE_FORMAT". + MatchesInboundSession(oneTimeKeyMsg string) (bool, error) + + // MatchesInboundSessionFrom checks if the PRE_KEY message is for this + // in-bound Session. This can happen if multiple messages are sent to this + // Account before this Account sends a message in reply. Returns true if + // the session matches. Returns false if the session does not match. + // Returns error on failure. If the base64 couldn't be decoded then the + // error will be "INVALID_BASE64". If the message was for an unsupported + // protocol version then the error will be "BAD_MESSAGE_VERSION". If the + // message couldn't be decoded then then the error will be + // "BAD_MESSAGE_FORMAT". + MatchesInboundSessionFrom(theirIdentityKey, oneTimeKeyMsg string) (bool, error) + + // EncryptMsgType returns the type of the next message that Encrypt will + // return. Returns MsgTypePreKey if the message will be a PRE_KEY message. + // Returns MsgTypeMsg if the message will be a normal message. + EncryptMsgType() id.OlmMsgType + + // Encrypt encrypts a message using the Session. Returns the encrypted + // message as base64. + Encrypt(plaintext []byte) (id.OlmMsgType, []byte, error) + + // Decrypt decrypts a message using the Session. Returns the plain-text on + // success. Returns error on failure. If the base64 couldn't be decoded + // then the error will be "INVALID_BASE64". If the message is for an + // unsupported version of the protocol then the error will be + // "BAD_MESSAGE_VERSION". If the message couldn't be decoded then the error + // will be BAD_MESSAGE_FORMAT". If the MAC on the message was invalid then + // the error will be "BAD_MESSAGE_MAC". + Decrypt(message string, msgType id.OlmMsgType) ([]byte, error) + + // Describe generates a string describing the internal state of an olm + // session for debugging and logging purposes. + Describe() string +} + +var InitSessionFromPickled func(pickled, key []byte) (Session, error) +var InitNewBlankSession func() Session + +// SessionFromPickled loads a Session from a pickled base64 string. Decrypts +// the Session using the supplied key. Returns error on failure. +func SessionFromPickled(pickled, key []byte) (Session, error) { + return InitSessionFromPickled(pickled, key) +} + +func NewBlankSession() Session { + return InitNewBlankSession() +} diff --git a/vendor/maunium.net/go/mautrix/crypto/pkcs7/pkcs7.go b/vendor/maunium.net/go/mautrix/crypto/pkcs7/pkcs7.go new file mode 100644 index 00000000..dc28ed6a --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/pkcs7/pkcs7.go @@ -0,0 +1,30 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pkcs7 + +import "bytes" + +// Pad implements PKCS#7 padding as defined in [RFC2315]. It pads the data to +// the given blockSize in the range [1, 255]. This is normally used in AES-CBC +// encryption. +// +// [RFC2315]: https://www.ietf.org/rfc/rfc2315.txt +func Pad(data []byte, blockSize int) []byte { + padding := blockSize - len(data)%blockSize + return append(data, bytes.Repeat([]byte{byte(padding)}, padding)...) +} + +// Unpad implements PKCS#7 unpadding as defined in [RFC2315]. It unpads the +// data by reading the padding amount from the last byte of the data. This is +// normally used in AES-CBC decryption. +// +// [RFC2315]: https://www.ietf.org/rfc/rfc2315.txt +func Unpad(data []byte) []byte { + length := len(data) + unpadding := int(data[length-1]) + return data[:length-unpadding] +} diff --git a/vendor/maunium.net/go/mautrix/crypto/signatures/signatures.go b/vendor/maunium.net/go/mautrix/crypto/signatures/signatures.go new file mode 100644 index 00000000..0c4422f9 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/signatures/signatures.go @@ -0,0 +1,94 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package signatures + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + "go.mau.fi/util/exgjson" + + "maunium.net/go/mautrix/crypto/canonicaljson" + "maunium.net/go/mautrix/crypto/goolm/crypto" + "maunium.net/go/mautrix/id" +) + +var ( + ErrEmptyInput = errors.New("empty input") + ErrSignatureNotFound = errors.New("input JSON doesn't contain signature from specified device") +) + +// Signatures represents a set of signatures for some data from multiple users +// and keys. +type Signatures map[id.UserID]map[id.KeyID]string + +// NewSingleSignature creates a new [Signatures] object with a single +// signature. +func NewSingleSignature(userID id.UserID, algorithm id.KeyAlgorithm, keyID string, signature string) Signatures { + return Signatures{ + userID: { + id.NewKeyID(algorithm, keyID): signature, + }, + } +} + +// VerifySignature verifies an Ed25519 signature. +func VerifySignature(message []byte, key id.Ed25519, signature []byte) (ok bool, err error) { + if len(message) == 0 || len(key) == 0 || len(signature) == 0 { + return false, ErrEmptyInput + } + keyDecoded, err := base64.RawStdEncoding.DecodeString(key.String()) + if err != nil { + return false, err + } + publicKey := crypto.Ed25519PublicKey(keyDecoded) + return publicKey.Verify(message, signature), nil +} + +// VerifySignatureJSON verifies the signature in the given JSON object "obj" +// as described in [Appendix 3] of the Matrix Spec. +// +// This function is a wrapper over [Utility.VerifySignatureJSON] that creates +// and destroys the [Utility] object transparently. +// +// If the "obj" is not already a [json.RawMessage], it will re-encoded as JSON +// for the verification, so "json" tags will be honored. +// +// [Appendix 3]: https://spec.matrix.org/v1.9/appendices/#signing-json +func VerifySignatureJSON(obj any, userID id.UserID, keyName string, key id.Ed25519) (bool, error) { + var err error + objJSON, ok := obj.(json.RawMessage) + if !ok { + objJSON, err = json.Marshal(obj) + if err != nil { + return false, err + } + } + + sig := gjson.GetBytes(objJSON, exgjson.Path("signatures", string(userID), fmt.Sprintf("ed25519:%s", keyName))) + if !sig.Exists() || sig.Type != gjson.String { + return false, ErrSignatureNotFound + } + objJSON, err = sjson.DeleteBytes(objJSON, "unsigned") + if err != nil { + return false, err + } + objJSON, err = sjson.DeleteBytes(objJSON, "signatures") + if err != nil { + return false, err + } + objJSONString := canonicaljson.CanonicalJSONAssumeValid(objJSON) + sigBytes, err := base64.RawStdEncoding.DecodeString(sig.Str) + if err != nil { + return false, err + } + return VerifySignature(objJSONString, key, sigBytes) +} diff --git a/vendor/maunium.net/go/mautrix/crypto/utils/utils.go b/vendor/maunium.net/go/mautrix/crypto/utils/utils.go new file mode 100644 index 00000000..e2f8a19c --- /dev/null +++ b/vendor/maunium.net/go/mautrix/crypto/utils/utils.go @@ -0,0 +1,132 @@ +// Copyright (c) 2020 Nikos Filippakis +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "encoding/base64" + "strings" + + "go.mau.fi/util/base58" + "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/pbkdf2" +) + +const ( + // AESCTRKeyLength is the length of the AES256-CTR key used. + AESCTRKeyLength = 32 + // AESCTRIVLength is the length of the AES256-CTR IV used. + AESCTRIVLength = 16 + // HMACKeyLength is the length of the HMAC key used. + HMACKeyLength = 32 + // SHAHashLength is the length of the SHA hash used. + SHAHashLength = 32 +) + +// XorA256CTR encrypts the input with the keystream generated by the AES256-CTR algorithm with the given arguments. +func XorA256CTR(source []byte, key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) []byte { + block, _ := aes.NewCipher(key[:]) + cipher.NewCTR(block, iv[:]).XORKeyStream(source, source) + return source +} + +// GenAttachmentA256CTR generates a new random AES256-CTR key and IV suitable for encrypting attachments. +func GenAttachmentA256CTR() (key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) { + _, err := rand.Read(key[:]) + if err != nil { + panic(err) + } + + // The last 8 bytes of the IV act as the counter in AES-CTR, which means they're left empty here + _, err = rand.Read(iv[:8]) + if err != nil { + panic(err) + } + return +} + +// GenA256CTRIV generates a random IV for AES256-CTR with the last bit set to zero. +func GenA256CTRIV() (iv [AESCTRIVLength]byte) { + _, err := rand.Read(iv[:]) + if err != nil { + panic(err) + } + iv[8] &= 0x7F + return +} + +// DeriveKeysSHA256 derives an AES and a HMAC key from the given recovery key. +func DeriveKeysSHA256(key []byte, name string) ([AESCTRKeyLength]byte, [HMACKeyLength]byte) { + var zeroBytes [32]byte + + derivedHkdf := hkdf.New(sha256.New, key[:], zeroBytes[:], []byte(name)) + + var aesKey [AESCTRKeyLength]byte + var hmacKey [HMACKeyLength]byte + derivedHkdf.Read(aesKey[:]) + derivedHkdf.Read(hmacKey[:]) + + return aesKey, hmacKey +} + +// PBKDF2SHA512 generates a key of the given bit-length using the given passphrase, salt and iteration count. +func PBKDF2SHA512(password []byte, salt []byte, iters int, keyLenBits int) []byte { + return pbkdf2.Key(password, salt, iters, keyLenBits/8, sha512.New) +} + +// DecodeBase58RecoveryKey recovers the secret storage from a recovery key. +func DecodeBase58RecoveryKey(recoveryKey string) []byte { + noSpaces := strings.ReplaceAll(recoveryKey, " ", "") + decoded := base58.Decode(noSpaces) + if len(decoded) != AESCTRKeyLength+3 { // AESCTRKeyLength bytes key and 3 bytes prefix / parity + return nil + } + var parity byte + for _, b := range decoded[:34] { + parity ^= b + } + if parity != decoded[34] || decoded[0] != 0x8B || decoded[1] != 1 { + return nil + } + return decoded[2:34] +} + +// EncodeBase58RecoveryKey recovers the secret storage from a recovery key. +func EncodeBase58RecoveryKey(key []byte) string { + var inputBytes [35]byte + copy(inputBytes[2:34], key[:]) + inputBytes[0] = 0x8B + inputBytes[1] = 1 + + var parity byte + for _, b := range inputBytes[:34] { + parity ^= b + } + inputBytes[34] = parity + recoveryKey := base58.Encode(inputBytes[:]) + + var spacedKey string + for i, c := range recoveryKey { + if i > 0 && i%4 == 0 { + spacedKey += " " + } + spacedKey += string(c) + } + return spacedKey +} + +// HMACSHA256B64 calculates the unpadded base64 of the SHA256 hmac of the input with the given key. +func HMACSHA256B64(input []byte, hmacKey [HMACKeyLength]byte) string { + h := hmac.New(sha256.New, hmacKey[:]) + h.Write(input) + return base64.RawStdEncoding.EncodeToString(h.Sum(nil)) +} diff --git a/vendor/maunium.net/go/mautrix/error.go b/vendor/maunium.net/go/mautrix/error.go new file mode 100644 index 00000000..b7c92a5f --- /dev/null +++ b/vendor/maunium.net/go/mautrix/error.go @@ -0,0 +1,198 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mautrix + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + + "go.mau.fi/util/exhttp" + "go.mau.fi/util/exmaps" + "golang.org/x/exp/maps" +) + +// Common error codes from https://matrix.org/docs/spec/client_server/latest#api-standards +// +// Can be used with errors.Is() to check the response code without casting the error: +// +// err := client.Sync() +// if errors.Is(err, MUnknownToken) { +// // logout +// } +var ( + // Generic error for when the server encounters an error and it does not have a more specific error code. + // Note that `errors.Is` will check the error message rather than code for M_UNKNOWNs. + MUnknown = RespError{ErrCode: "M_UNKNOWN", StatusCode: http.StatusInternalServerError} + // Forbidden access, e.g. joining a room without permission, failed login. + MForbidden = RespError{ErrCode: "M_FORBIDDEN", StatusCode: http.StatusForbidden} + // Unrecognized request, e.g. the endpoint does not exist or is not implemented. + MUnrecognized = RespError{ErrCode: "M_UNRECOGNIZED", StatusCode: http.StatusNotFound} + // The access token specified was not recognised. + MUnknownToken = RespError{ErrCode: "M_UNKNOWN_TOKEN", StatusCode: http.StatusUnauthorized} + // No access token was specified for the request. + MMissingToken = RespError{ErrCode: "M_MISSING_TOKEN", StatusCode: http.StatusUnauthorized} + // Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys. + MBadJSON = RespError{ErrCode: "M_BAD_JSON", StatusCode: http.StatusBadRequest} + // Request did not contain valid JSON. + MNotJSON = RespError{ErrCode: "M_NOT_JSON", StatusCode: http.StatusBadRequest} + // No resource was found for this request. + MNotFound = RespError{ErrCode: "M_NOT_FOUND", StatusCode: http.StatusNotFound} + // Too many requests have been sent in a short period of time. Wait a while then try again. + MLimitExceeded = RespError{ErrCode: "M_LIMIT_EXCEEDED", StatusCode: http.StatusTooManyRequests} + // The user ID associated with the request has been deactivated. + // Typically for endpoints that prove authentication, such as /login. + MUserDeactivated = RespError{ErrCode: "M_USER_DEACTIVATED"} + // Encountered when trying to register a user ID which has been taken. + MUserInUse = RespError{ErrCode: "M_USER_IN_USE", StatusCode: http.StatusBadRequest} + // Encountered when trying to register a user ID which is not valid. + MInvalidUsername = RespError{ErrCode: "M_INVALID_USERNAME", StatusCode: http.StatusBadRequest} + // Sent when the room alias given to the createRoom API is already in use. + MRoomInUse = RespError{ErrCode: "M_ROOM_IN_USE", StatusCode: http.StatusBadRequest} + // The state change requested cannot be performed, such as attempting to unban a user who is not banned. + MBadState = RespError{ErrCode: "M_BAD_STATE"} + // The request or entity was too large. + MTooLarge = RespError{ErrCode: "M_TOO_LARGE", StatusCode: http.StatusRequestEntityTooLarge} + // The resource being requested is reserved by an application service, or the application service making the request has not created the resource. + MExclusive = RespError{ErrCode: "M_EXCLUSIVE", StatusCode: http.StatusBadRequest} + // The client's request to create a room used a room version that the server does not support. + MUnsupportedRoomVersion = RespError{ErrCode: "M_UNSUPPORTED_ROOM_VERSION"} + // The client attempted to join a room that has a version the server does not support. + // Inspect the room_version property of the error response for the room's version. + MIncompatibleRoomVersion = RespError{ErrCode: "M_INCOMPATIBLE_ROOM_VERSION"} + // The client specified a parameter that has the wrong value. + MInvalidParam = RespError{ErrCode: "M_INVALID_PARAM", StatusCode: http.StatusBadRequest} + + MURLNotSet = RespError{ErrCode: "M_URL_NOT_SET"} + MBadStatus = RespError{ErrCode: "M_BAD_STATUS"} + MConnectionTimeout = RespError{ErrCode: "M_CONNECTION_TIMEOUT"} + MConnectionFailed = RespError{ErrCode: "M_CONNECTION_FAILED"} + + MUnredactedContentDeleted = RespError{ErrCode: "FI.MAU.MSC2815_UNREDACTED_CONTENT_DELETED"} + MUnredactedContentNotReceived = RespError{ErrCode: "FI.MAU.MSC2815_UNREDACTED_CONTENT_NOT_RECEIVED"} +) + +var ( + ErrClientIsNil = errors.New("client is nil") + ErrClientHasNoHomeserver = errors.New("client has no homeserver set") +) + +// HTTPError An HTTP Error response, which may wrap an underlying native Go Error. +type HTTPError struct { + Request *http.Request + Response *http.Response + ResponseBody string + + WrappedError error + RespError *RespError + Message string +} + +func (e HTTPError) Is(err error) bool { + return (e.RespError != nil && errors.Is(e.RespError, err)) || (e.WrappedError != nil && errors.Is(e.WrappedError, err)) +} + +func (e HTTPError) IsStatus(code int) bool { + return e.Response != nil && e.Response.StatusCode == code +} + +func (e HTTPError) Error() string { + if e.WrappedError != nil { + return fmt.Sprintf("%s: %v", e.Message, e.WrappedError) + } else if e.RespError != nil { + return fmt.Sprintf("%s (HTTP %d): %s", e.RespError.ErrCode, e.Response.StatusCode, e.RespError.Err) + } else { + msg := fmt.Sprintf("HTTP %d", e.Response.StatusCode) + if len(e.ResponseBody) > 0 { + msg = fmt.Sprintf("%s: %s", msg, e.ResponseBody) + } + return msg + } +} + +func (e HTTPError) Unwrap() error { + if e.WrappedError != nil { + return e.WrappedError + } else if e.RespError != nil { + return *e.RespError + } + return nil +} + +// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface. +// See https://spec.matrix.org/v1.2/client-server-api/#api-standards +type RespError struct { + ErrCode string + Err string + ExtraData map[string]any + + StatusCode int +} + +func (e *RespError) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &e.ExtraData) + if err != nil { + return err + } + e.ErrCode, _ = e.ExtraData["errcode"].(string) + e.Err, _ = e.ExtraData["error"].(string) + return nil +} + +func (e *RespError) MarshalJSON() ([]byte, error) { + data := exmaps.NonNilClone(e.ExtraData) + data["errcode"] = e.ErrCode + data["error"] = e.Err + return json.Marshal(data) +} + +func (e RespError) Write(w http.ResponseWriter) { + if w == nil { + return + } + statusCode := e.StatusCode + if statusCode == 0 { + statusCode = http.StatusInternalServerError + } + exhttp.WriteJSONResponse(w, statusCode, &e) +} + +func (e RespError) WithMessage(msg string, args ...any) RespError { + if len(args) > 0 { + msg = fmt.Sprintf(msg, args...) + } + e.Err = msg + return e +} + +func (e RespError) WithStatus(status int) RespError { + e.StatusCode = status + return e +} + +func (e RespError) WithExtraData(extraData map[string]any) RespError { + e.ExtraData = exmaps.NonNilClone(e.ExtraData) + maps.Copy(e.ExtraData, extraData) + return e +} + +// Error returns the errcode and error message. +func (e RespError) Error() string { + return e.ErrCode + ": " + e.Err +} + +func (e RespError) Is(err error) bool { + e2, ok := err.(RespError) + if !ok { + return false + } + if e.ErrCode == "M_UNKNOWN" && e2.ErrCode == "M_UNKNOWN" { + return e.Err == e2.Err + } + return e2.ErrCode == e.ErrCode +} diff --git a/vendor/maunium.net/go/mautrix/event/accountdata.go b/vendor/maunium.net/go/mautrix/event/accountdata.go new file mode 100644 index 00000000..223919a1 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/accountdata.go @@ -0,0 +1,119 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "strings" + "time" + + "maunium.net/go/mautrix/id" +) + +// TagEventContent represents the content of a m.tag room account data event. +// https://spec.matrix.org/v1.2/client-server-api/#mtag +type TagEventContent struct { + Tags Tags `json:"tags"` +} + +type Tags map[RoomTag]TagMetadata + +type RoomTag string + +const ( + RoomTagFavourite RoomTag = "m.favourite" + RoomTagLowPriority RoomTag = "m.lowpriority" + RoomTagServerNotice RoomTag = "m.server_notice" +) + +func (rt RoomTag) IsUserDefined() bool { + return strings.HasPrefix(string(rt), "u.") +} + +func (rt RoomTag) String() string { + return string(rt) +} + +func (rt RoomTag) Name() string { + if rt.IsUserDefined() { + return string(rt[2:]) + } + switch rt { + case RoomTagFavourite: + return "Favourite" + case RoomTagLowPriority: + return "Low priority" + case RoomTagServerNotice: + return "Server notice" + default: + return "" + } +} + +// Deprecated: type alias +type Tag = TagMetadata + +type TagMetadata struct { + Order json.Number `json:"order,omitempty"` + + MauDoublePuppetSource string `json:"fi.mau.double_puppet_source,omitempty"` +} + +// DirectChatsEventContent represents the content of a m.direct account data event. +// https://spec.matrix.org/v1.2/client-server-api/#mdirect +type DirectChatsEventContent map[id.UserID][]id.RoomID + +// FullyReadEventContent represents the content of a m.fully_read account data event. +// https://spec.matrix.org/v1.2/client-server-api/#mfully_read +type FullyReadEventContent struct { + EventID id.EventID `json:"event_id"` +} + +// IgnoredUserListEventContent represents the content of a m.ignored_user_list account data event. +// https://spec.matrix.org/v1.2/client-server-api/#mignored_user_list +type IgnoredUserListEventContent struct { + IgnoredUsers map[id.UserID]IgnoredUser `json:"ignored_users"` +} + +type IgnoredUser struct { + // This is an empty object +} + +type MarkedUnreadEventContent struct { + Unread bool `json:"unread"` +} + +type BeeperMuteEventContent struct { + MutedUntil int64 `json:"muted_until,omitempty"` +} + +func (bmec *BeeperMuteEventContent) IsMuted() bool { + return bmec.MutedUntil < 0 || (bmec.MutedUntil > 0 && bmec.GetMutedUntilTime().After(time.Now())) +} + +var MutedForever = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.UTC) + +func (bmec *BeeperMuteEventContent) GetMutedUntilTime() time.Time { + if bmec.MutedUntil < 0 { + return MutedForever + } else if bmec.MutedUntil > 0 { + return time.UnixMilli(bmec.MutedUntil) + } + return time.Time{} +} + +func (bmec *BeeperMuteEventContent) GetMuteDuration() time.Duration { + ts := bmec.GetMutedUntilTime() + now := time.Now() + if ts.Before(now) { + return 0 + } else if ts == MutedForever { + return -1 + } else { + return ts.Sub(now) + } +} diff --git a/vendor/maunium.net/go/mautrix/event/audio.go b/vendor/maunium.net/go/mautrix/event/audio.go new file mode 100644 index 00000000..9eeb8edb --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/audio.go @@ -0,0 +1,21 @@ +package event + +import ( + "encoding/json" +) + +type MSC1767Audio struct { + Duration int `json:"duration"` + Waveform []int `json:"waveform"` +} + +type serializableMSC1767Audio MSC1767Audio + +func (ma *MSC1767Audio) MarshalJSON() ([]byte, error) { + if ma.Waveform == nil { + ma.Waveform = []int{} + } + return json.Marshal((*serializableMSC1767Audio)(ma)) +} + +type MSC3245Voice struct{} diff --git a/vendor/maunium.net/go/mautrix/event/beeper.go b/vendor/maunium.net/go/mautrix/event/beeper.go new file mode 100644 index 00000000..95b4a571 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/beeper.go @@ -0,0 +1,289 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/base32" + "encoding/binary" + "encoding/json" + "fmt" + "html" + "regexp" + "strconv" + "strings" + + "maunium.net/go/mautrix/id" +) + +type MessageStatusReason string + +const ( + MessageStatusGenericError MessageStatusReason = "m.event_not_handled" + MessageStatusUnsupported MessageStatusReason = "com.beeper.unsupported_event" + MessageStatusUndecryptable MessageStatusReason = "com.beeper.undecryptable_event" + MessageStatusTooOld MessageStatusReason = "m.event_too_old" + MessageStatusNetworkError MessageStatusReason = "m.foreign_network_error" + MessageStatusNoPermission MessageStatusReason = "m.no_permission" + MessageStatusBridgeUnavailable MessageStatusReason = "m.bridge_unavailable" +) + +type MessageStatus string + +const ( + MessageStatusSuccess MessageStatus = "SUCCESS" + MessageStatusPending MessageStatus = "PENDING" + MessageStatusRetriable MessageStatus = "FAIL_RETRIABLE" + MessageStatusFail MessageStatus = "FAIL_PERMANENT" +) + +type BeeperMessageStatusEventContent struct { + Network string `json:"network,omitempty"` + RelatesTo RelatesTo `json:"m.relates_to"` + Status MessageStatus `json:"status"` + Reason MessageStatusReason `json:"reason,omitempty"` + // Deprecated: clients were showing this to users even though they aren't supposed to. + // Use InternalError for error messages that should be included in bug reports, but not shown in the UI. + Error string `json:"error,omitempty"` + InternalError string `json:"internal_error,omitempty"` + Message string `json:"message,omitempty"` + + LastRetry id.EventID `json:"last_retry,omitempty"` + + MutateEventKey string `json:"mutate_event_key,omitempty"` + + // Indicates the set of users to whom the event was delivered. If nil, then + // the client should not expect delivered status at any later point. If not + // nil (even if empty), this field indicates which users the event was + // delivered to. + DeliveredToUsers *[]id.UserID `json:"delivered_to_users,omitempty"` +} + +type BeeperRelatesTo struct { + EventID id.EventID `json:"event_id,omitempty"` + RoomID id.RoomID `json:"room_id,omitempty"` + Type RelationType `json:"rel_type,omitempty"` +} + +type BeeperTranscriptionEventContent struct { + Text []ExtensibleText `json:"m.text,omitempty"` + Model string `json:"com.beeper.transcription.model,omitempty"` + RelatesTo BeeperRelatesTo `json:"com.beeper.relates_to,omitempty"` +} + +type BeeperRetryMetadata struct { + OriginalEventID id.EventID `json:"original_event_id"` + RetryCount int `json:"retry_count"` + // last_retry is also present, but not used by bridges +} + +type BeeperRoomKeyAckEventContent struct { + RoomID id.RoomID `json:"room_id"` + SessionID id.SessionID `json:"session_id"` + FirstMessageIndex int `json:"first_message_index"` +} + +type BeeperChatDeleteEventContent struct { + DeleteForEveryone bool `json:"delete_for_everyone,omitempty"` +} + +type IntOrString int + +func (ios *IntOrString) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '"' { + var str string + err := json.Unmarshal(data, &str) + if err != nil { + return err + } + intVal, err := strconv.Atoi(str) + if err != nil { + return err + } + *ios = IntOrString(intVal) + return nil + } + return json.Unmarshal(data, (*int)(ios)) +} + +type LinkPreview struct { + CanonicalURL string `json:"og:url,omitempty"` + Title string `json:"og:title,omitempty"` + Type string `json:"og:type,omitempty"` + Description string `json:"og:description,omitempty"` + SiteName string `json:"og:site_name,omitempty"` + + ImageURL id.ContentURIString `json:"og:image,omitempty"` + + ImageSize IntOrString `json:"matrix:image:size,omitempty"` + ImageWidth IntOrString `json:"og:image:width,omitempty"` + ImageHeight IntOrString `json:"og:image:height,omitempty"` + ImageType string `json:"og:image:type,omitempty"` +} + +// BeeperLinkPreview contains the data for a bundled URL preview as specified in MSC4095 +// +// https://github.com/matrix-org/matrix-spec-proposals/pull/4095 +type BeeperLinkPreview struct { + LinkPreview + + MatchedURL string `json:"matched_url,omitempty"` + ImageEncryption *EncryptedFileInfo `json:"beeper:image:encryption,omitempty"` +} + +type BeeperProfileExtra struct { + RemoteID string `json:"com.beeper.bridge.remote_id,omitempty"` + Identifiers []string `json:"com.beeper.bridge.identifiers,omitempty"` + Service string `json:"com.beeper.bridge.service,omitempty"` + Network string `json:"com.beeper.bridge.network,omitempty"` + IsBridgeBot bool `json:"com.beeper.bridge.is_bridge_bot,omitempty"` + IsNetworkBot bool `json:"com.beeper.bridge.is_network_bot,omitempty"` +} + +type BeeperPerMessageProfile struct { + ID string `json:"id"` + Displayname string `json:"displayname,omitempty"` + AvatarURL *id.ContentURIString `json:"avatar_url,omitempty"` + AvatarFile *EncryptedFileInfo `json:"avatar_file,omitempty"` + HasFallback bool `json:"has_fallback,omitempty"` +} + +func (content *MessageEventContent) AddPerMessageProfileFallback() { + if content.BeeperPerMessageProfile == nil || content.BeeperPerMessageProfile.HasFallback || content.BeeperPerMessageProfile.Displayname == "" { + return + } + content.BeeperPerMessageProfile.HasFallback = true + content.EnsureHasHTML() + content.Body = fmt.Sprintf("%s: %s", content.BeeperPerMessageProfile.Displayname, content.Body) + content.FormattedBody = fmt.Sprintf( + "%s: %s", + html.EscapeString(content.BeeperPerMessageProfile.Displayname), + content.FormattedBody, + ) +} + +var HTMLProfileFallbackRegex = regexp.MustCompile(`([^<]+): `) + +func (content *MessageEventContent) RemovePerMessageProfileFallback() { + if content.NewContent != nil && content.NewContent != content { + content.NewContent.RemovePerMessageProfileFallback() + } + if content == nil || content.BeeperPerMessageProfile == nil || !content.BeeperPerMessageProfile.HasFallback || content.BeeperPerMessageProfile.Displayname == "" { + return + } + content.BeeperPerMessageProfile.HasFallback = false + content.Body = strings.TrimPrefix(content.Body, content.BeeperPerMessageProfile.Displayname+": ") + if content.Format == FormatHTML { + content.FormattedBody = HTMLProfileFallbackRegex.ReplaceAllLiteralString(content.FormattedBody, "") + } +} + +type BeeperEncodedOrder struct { + order int64 + suborder int16 +} + +func NewBeeperEncodedOrder(order int64, suborder int16) *BeeperEncodedOrder { + return &BeeperEncodedOrder{order: order, suborder: suborder} +} + +func BeeperEncodedOrderFromString(str string) (*BeeperEncodedOrder, error) { + order, suborder, err := decodeIntPair(str) + if err != nil { + return nil, err + } + return &BeeperEncodedOrder{order: order, suborder: suborder}, nil +} + +func (b *BeeperEncodedOrder) String() string { + if b == nil { + return "" + } + return encodeIntPair(b.order, b.suborder) +} + +func (b *BeeperEncodedOrder) OrderPair() (int64, int16) { + if b == nil { + return 0, 0 + } + return b.order, b.suborder +} + +func (b *BeeperEncodedOrder) IsZero() bool { + return b == nil || (b.order == 0 && b.suborder == 0) +} + +func (b *BeeperEncodedOrder) MarshalJSON() ([]byte, error) { + return []byte(`"` + b.String() + `"`), nil +} + +func (b *BeeperEncodedOrder) UnmarshalJSON(data []byte) error { + if b == nil { + return fmt.Errorf("BeeperEncodedOrder: receiver is nil") + } + str := string(data) + if len(str) < 2 { + return fmt.Errorf("invalid encoded order string: %s", str) + } + decoded, err := BeeperEncodedOrderFromString(str[1 : len(str)-1]) + if err != nil { + return err + } + b.order, b.suborder = decoded.order, decoded.suborder + return nil +} + +// encodeIntPair encodes an int64 and an int16 into a lexicographically sortable string +func encodeIntPair(a int64, b int16) string { + // Create a buffer to hold the binary representation of the integers. + // Will need 8 bytes for the int64 and 2 bytes for the int16. + var buf [10]byte + + // Flip the sign bit of each integer to map the entire int range to uint + // in a way that preserves the order of the original integers. + // + // Explanation: + // - By XORing with (1 << 63), we flip the most significant bit (sign bit) of the int64 value. + // - Negative numbers (which have a sign bit of 1) become smaller uint64 values. + // - Non-negative numbers (with a sign bit of 0) become larger uint64 values. + // - This mapping preserves the original ordering when the uint64 values are compared. + binary.BigEndian.PutUint64(buf[0:8], uint64(a)^(1<<63)) + binary.BigEndian.PutUint16(buf[8:10], uint16(b)^(1<<15)) + + // Encode the buffer into a Base32 string without padding using the Hex encoding. + // + // Explanation: + // - Base32 encoding converts binary data into a text representation using 32 ASCII characters. + // - Using Base32HexEncoding ensures that the characters are in lexicographical order. + // - Disabling padding results in a consistent string length, which is important for sorting. + encoded := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(buf[:]) + + return encoded +} + +// decodeIntPair decodes a string produced by encodeIntPair back into the original int64 and int16 values +func decodeIntPair(encoded string) (int64, int16, error) { + // Decode the Base32 string back into the original byte buffer. + buf, err := base32.HexEncoding.WithPadding(base32.NoPadding).DecodeString(encoded) + if err != nil { + return 0, 0, fmt.Errorf("failed to decode string: %w", err) + } + + // Check that the decoded buffer has the expected length. + if len(buf) != 10 { + return 0, 0, fmt.Errorf("invalid encoded string length: expected 10 bytes, got %d", len(buf)) + } + + // Read the uint values from the buffer using big-endian byte order. + aPos := binary.BigEndian.Uint64(buf[0:8]) + bPos := binary.BigEndian.Uint16(buf[8:10]) + + // Reverse the sign bit flip to retrieve the original values. + a := int64(aPos ^ (1 << 63)) + b := int16(bPos ^ (1 << 15)) + + return a, b, nil +} diff --git a/vendor/maunium.net/go/mautrix/event/botcommand.go b/vendor/maunium.net/go/mautrix/event/botcommand.go new file mode 100644 index 00000000..2b208656 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/botcommand.go @@ -0,0 +1,49 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" +) + +type BotCommandsEventContent struct { + Sigil string `json:"sigil,omitempty"` + Commands []*BotCommand `json:"commands,omitempty"` +} + +type BotCommand struct { + Syntax string `json:"syntax"` + Aliases []string `json:"fi.mau.aliases,omitempty"` // Not in MSC (yet) + Arguments []*BotCommandArgument `json:"arguments,omitempty"` + Description *ExtensibleTextContainer `json:"description,omitempty"` +} + +type BotArgumentType string + +const ( + BotArgumentTypeString BotArgumentType = "string" + BotArgumentTypeEnum BotArgumentType = "enum" + BotArgumentTypeInteger BotArgumentType = "integer" + BotArgumentTypeBoolean BotArgumentType = "boolean" + BotArgumentTypeUserID BotArgumentType = "user_id" + BotArgumentTypeRoomID BotArgumentType = "room_id" + BotArgumentTypeRoomAlias BotArgumentType = "room_alias" + BotArgumentTypeEventID BotArgumentType = "event_id" +) + +type BotCommandArgument struct { + Type BotArgumentType `json:"type"` + DefaultValue any `json:"fi.mau.default_value,omitempty"` // Not in MSC (yet) + Description *ExtensibleTextContainer `json:"description,omitempty"` + Enum []string `json:"enum,omitempty"` + Variadic bool `json:"variadic,omitempty"` +} + +type BotCommandInput struct { + Syntax string `json:"syntax"` + Arguments json.RawMessage `json:"arguments,omitempty"` +} diff --git a/vendor/maunium.net/go/mautrix/event/capabilities.d.ts b/vendor/maunium.net/go/mautrix/event/capabilities.d.ts new file mode 100644 index 00000000..37848575 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/capabilities.d.ts @@ -0,0 +1,188 @@ +/** + * The content of the `com.beeper.room_features` state event. + */ +export interface RoomFeatures { + /** + * Supported formatting features. If omitted, no formatting is supported. + * + * Capability level 0 means the corresponding HTML tags/attributes are ignored + * and will be treated as if they don't exist, which means that children will + * be rendered, but attributes will be dropped. + */ + formatting?: Record + /** + * Supported file message types and their features. + * + * If a message type isn't listed here, it should be treated as support level -2 (will be rejected). + */ + file?: Record + + /** Maximum length of normal text messages. */ + max_text_length?: integer + + /** Whether location messages (`m.location`) are supported. */ + location_message?: CapabilitySupportLevel + /** Whether polls are supported. */ + poll?: CapabilitySupportLevel + /** Whether replying in a thread is supported. */ + thread?: CapabilitySupportLevel + /** Whether replying to a specific message is supported. */ + reply?: CapabilitySupportLevel + + /** Whether edits are supported. */ + edit?: CapabilitySupportLevel + /** How many times can an individual message be edited. */ + edit_max_count?: integer + /** How old messages can be edited, in seconds. */ + edit_max_age?: seconds + /** Whether deleting messages for everyone is supported */ + delete?: CapabilitySupportLevel + /** How old messages can be deleted for everyone, in seconds. */ + delete_max_age?: seconds + /** Whether deleting messages just for yourself is supported. No message age limit. */ + delete_for_me?: boolean + /** Allowed configuration options for disappearing timers. */ + disappearing_timer?: DisappearingTimerCapability + + /** Whether reactions are supported. */ + reaction?: CapabilitySupportLevel + /** How many reactions can be added to a single message. */ + reaction_count?: integer + /** + * The Unicode emojis allowed for reactions. If omitted, all emojis are allowed. + * Emojis in this list must include variation selector 16 if allowed in the Unicode spec. + */ + allowed_reactions?: string[] + /** Whether custom emoji reactions are allowed. */ + custom_emoji_reactions?: boolean + + /** Whether deleting the chat for yourself is supported. */ + delete_chat?: boolean + /** Whether deleting the chat for all participants is supported. */ + delete_chat_for_everyone?: boolean +} + +declare type integer = number +declare type seconds = integer +declare type milliseconds = integer +declare type MIMEClass = "image" | "audio" | "video" | "text" | "font" | "model" | "application" +declare type MIMETypeOrPattern = + "*/*" + | `${MIMEClass}/*` + | `${MIMEClass}/${string}` + | `${MIMEClass}/${string}; ${string}` + +export enum CapabilityMsgType { + // Real message types used in the `msgtype` field + Image = "m.image", + File = "m.file", + Audio = "m.audio", + Video = "m.video", + + // Pseudo types only used in capabilities + /** An `m.audio` message that has `"org.matrix.msc3245.voice": {}` */ + Voice = "org.matrix.msc3245.voice", + /** An `m.video` message that has `"info": {"fi.mau.gif": true}`, or an `m.image` message of type `image/gif` */ + GIF = "fi.mau.gif", + /** An `m.sticker` event, no `msgtype` field */ + Sticker = "m.sticker", +} + +export interface FileFeatures { + /** + * The supported MIME types or type patterns and their support levels. + * + * If a mime type doesn't match any pattern provided, + * it should be treated as support level -2 (will be rejected). + */ + mime_types: Record + + /** The support level for captions within this file message type */ + caption?: CapabilitySupportLevel + /** The maximum length for captions (only applicable if captions are supported). */ + max_caption_length?: integer + /** The maximum file size as bytes. */ + max_size?: integer + /** For images and videos, the maximum width as pixels. */ + max_width?: integer + /** For images and videos, the maximum height as pixels. */ + max_height?: integer + /** For videos and audio files, the maximum duration as seconds. */ + max_duration?: seconds + + /** Can this type of file be sent as view-once media? */ + view_once?: boolean +} + +export enum DisappearingType { + None = "", + AfterRead = "after_read", + AfterSend = "after_send", +} + +export interface DisappearingTimerCapability { + types: DisappearingType[] + /** Allowed timer values. If omitted, any timer is allowed. */ + timers?: milliseconds[] + /** + * Whether clients should omit the empty disappearing_timer object in messages that they don't want to disappear + * + * Generally, bridged rooms will want the object to be always present, while native Matrix rooms don't, + * so the hardcoded features for Matrix rooms should set this to true, while bridges will not. + */ + omit_empty_timer?: true +} + +/** + * The support level for a feature. These are integers rather than booleans + * to accurately represent what the bridge is doing and hopefully make the + * state event more generally useful. Our clients should check for > 0 to + * determine if the feature should be allowed. + */ +export enum CapabilitySupportLevel { + /** The feature is unsupported and messages using it will be rejected. */ + Rejected = -2, + /** The feature is unsupported and has no fallback. The message will go through, but data may be lost. */ + Dropped = -1, + /** The feature is unsupported, but may have a fallback. The nature of the fallback depends on the context. */ + Unsupported = 0, + /** The feature is partially supported (e.g. it may be converted to a different format). */ + PartialSupport = 1, + /** The feature is fully supported and can be safely used. */ + FullySupported = 2, +} + +/** + * A formatting feature that consists of specific HTML tags and/or attributes. + */ +export enum FormattingFeature { + Bold = "bold", // strong, b + Italic = "italic", // em, i + Underline = "underline", // u + Strikethrough = "strikethrough", // del, s + InlineCode = "inline_code", // code + CodeBlock = "code_block", // pre + code + SyntaxHighlighting = "code_block.syntax_highlighting", //

+	Blockquote = "blockquote", // blockquote
+	InlineLink = "inline_link", // a
+	UserLink = "user_link", // 
+	RoomLink = "room_link", // 
+	EventLink = "event_link", // 
+	AtRoomMention = "at_room_mention", // @room (no html tag)
+	UnorderedList = "unordered_list", // ul + li
+	OrderedList = "ordered_list", // ol + li
+	ListStart = "ordered_list.start", // 
    + ListJumpValue = "ordered_list.jump_value", //
  1. + CustomEmoji = "custom_emoji", // + Spoiler = "spoiler", // + SpoilerReason = "spoiler.reason", // + TextForegroundColor = "color.foreground", // + TextBackgroundColor = "color.background", // + HorizontalLine = "horizontal_line", // hr + Headers = "headers", // h1, h2, h3, h4, h5, h6 + Superscript = "superscript", // sup + Subscript = "subscript", // sub + Math = "math", // + DetailsSummary = "details_summary", //
    ......
    + Table = "table", // table, thead, tbody, tr, th, td +} diff --git a/vendor/maunium.net/go/mautrix/event/capabilities.go b/vendor/maunium.net/go/mautrix/event/capabilities.go new file mode 100644 index 00000000..42afe5b6 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/capabilities.go @@ -0,0 +1,341 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/binary" + "fmt" + "io" + "mime" + "slices" + "strings" + + "go.mau.fi/util/exerrors" + "go.mau.fi/util/jsontime" + "go.mau.fi/util/ptr" + "golang.org/x/exp/constraints" + "golang.org/x/exp/maps" +) + +type RoomFeatures struct { + ID string `json:"id,omitempty"` + + // N.B. New fields need to be added to the Hash function to be included in the deduplication hash. + + Formatting FormattingFeatureMap `json:"formatting,omitempty"` + File FileFeatureMap `json:"file,omitempty"` + + MaxTextLength int `json:"max_text_length,omitempty"` + + LocationMessage CapabilitySupportLevel `json:"location_message,omitempty"` + Poll CapabilitySupportLevel `json:"poll,omitempty"` + Thread CapabilitySupportLevel `json:"thread,omitempty"` + Reply CapabilitySupportLevel `json:"reply,omitempty"` + + Edit CapabilitySupportLevel `json:"edit,omitempty"` + EditMaxCount int `json:"edit_max_count,omitempty"` + EditMaxAge *jsontime.Seconds `json:"edit_max_age,omitempty"` + Delete CapabilitySupportLevel `json:"delete,omitempty"` + DeleteForMe bool `json:"delete_for_me,omitempty"` + DeleteMaxAge *jsontime.Seconds `json:"delete_max_age,omitempty"` + + DisappearingTimer *DisappearingTimerCapability `json:"disappearing_timer,omitempty"` + + Reaction CapabilitySupportLevel `json:"reaction,omitempty"` + ReactionCount int `json:"reaction_count,omitempty"` + AllowedReactions []string `json:"allowed_reactions,omitempty"` + CustomEmojiReactions bool `json:"custom_emoji_reactions,omitempty"` + + ReadReceipts bool `json:"read_receipts,omitempty"` + TypingNotifications bool `json:"typing_notifications,omitempty"` + Archive bool `json:"archive,omitempty"` + MarkAsUnread bool `json:"mark_as_unread,omitempty"` + DeleteChat bool `json:"delete_chat,omitempty"` + DeleteChatForEveryone bool `json:"delete_chat_for_everyone,omitempty"` +} + +func (rf *RoomFeatures) GetID() string { + if rf.ID != "" { + return rf.ID + } + return base64.RawURLEncoding.EncodeToString(rf.Hash()) +} + +func (rf *RoomFeatures) Clone() *RoomFeatures { + if rf == nil { + return nil + } + clone := *rf + clone.File = clone.File.Clone() + clone.Formatting = maps.Clone(clone.Formatting) + clone.EditMaxAge = ptr.Clone(clone.EditMaxAge) + clone.DeleteMaxAge = ptr.Clone(clone.DeleteMaxAge) + clone.DisappearingTimer = clone.DisappearingTimer.Clone() + clone.AllowedReactions = slices.Clone(clone.AllowedReactions) + return &clone +} + +type FormattingFeatureMap map[FormattingFeature]CapabilitySupportLevel + +type FileFeatureMap map[CapabilityMsgType]*FileFeatures + +func (ffm FileFeatureMap) Clone() FileFeatureMap { + dup := maps.Clone(ffm) + for key, value := range dup { + dup[key] = value.Clone() + } + return dup +} + +type DisappearingTimerCapability struct { + Types []DisappearingType `json:"types"` + Timers []jsontime.Milliseconds `json:"timers,omitempty"` + + OmitEmptyTimer bool `json:"omit_empty_timer,omitempty"` +} + +func (dtc *DisappearingTimerCapability) Clone() *DisappearingTimerCapability { + if dtc == nil { + return nil + } + clone := *dtc + clone.Types = slices.Clone(clone.Types) + clone.Timers = slices.Clone(clone.Timers) + return &clone +} + +func (dtc *DisappearingTimerCapability) Supports(content *BeeperDisappearingTimer) bool { + if dtc == nil || content == nil || content.Type == DisappearingTypeNone { + return true + } + return slices.Contains(dtc.Types, content.Type) && (dtc.Timers == nil || slices.Contains(dtc.Timers, content.Timer)) +} + +type CapabilityMsgType = MessageType + +// Message types which are used for event capability signaling, but aren't real values for the msgtype field. +const ( + CapMsgVoice CapabilityMsgType = "org.matrix.msc3245.voice" + CapMsgGIF CapabilityMsgType = "fi.mau.gif" + CapMsgSticker CapabilityMsgType = "m.sticker" +) + +type CapabilitySupportLevel int + +func (csl CapabilitySupportLevel) Partial() bool { + return csl >= CapLevelPartialSupport +} + +func (csl CapabilitySupportLevel) Full() bool { + return csl >= CapLevelFullySupported +} + +func (csl CapabilitySupportLevel) Reject() bool { + return csl <= CapLevelRejected +} + +const ( + CapLevelRejected CapabilitySupportLevel = -2 // The feature is unsupported and messages using it will be rejected. + CapLevelDropped CapabilitySupportLevel = -1 // The feature is unsupported and has no fallback. The message will go through, but data may be lost. + CapLevelUnsupported CapabilitySupportLevel = 0 // The feature is unsupported, but may have a fallback. + CapLevelPartialSupport CapabilitySupportLevel = 1 // The feature is partially supported (e.g. it may be converted to a different format). + CapLevelFullySupported CapabilitySupportLevel = 2 // The feature is fully supported and can be safely used. +) + +type FormattingFeature string + +const ( + FmtBold FormattingFeature = "bold" // strong, b + FmtItalic FormattingFeature = "italic" // em, i + FmtUnderline FormattingFeature = "underline" // u + FmtStrikethrough FormattingFeature = "strikethrough" // del, s + FmtInlineCode FormattingFeature = "inline_code" // code + FmtCodeBlock FormattingFeature = "code_block" // pre + code + FmtSyntaxHighlighting FormattingFeature = "code_block.syntax_highlighting" //
    
    +	FmtBlockquote          FormattingFeature = "blockquote"                     // blockquote
    +	FmtInlineLink          FormattingFeature = "inline_link"                    // a
    +	FmtUserLink            FormattingFeature = "user_link"                      // 
    +	FmtRoomLink            FormattingFeature = "room_link"                      // 
    +	FmtEventLink           FormattingFeature = "event_link"                     // 
    +	FmtAtRoomMention       FormattingFeature = "at_room_mention"                // @room (no html tag)
    +	FmtUnorderedList       FormattingFeature = "unordered_list"                 // ul + li
    +	FmtOrderedList         FormattingFeature = "ordered_list"                   // ol + li
    +	FmtListStart           FormattingFeature = "ordered_list.start"             // 
      + FmtListJumpValue FormattingFeature = "ordered_list.jump_value" //
    1. + FmtCustomEmoji FormattingFeature = "custom_emoji" // + FmtSpoiler FormattingFeature = "spoiler" // + FmtSpoilerReason FormattingFeature = "spoiler.reason" // + FmtTextForegroundColor FormattingFeature = "color.foreground" // + FmtTextBackgroundColor FormattingFeature = "color.background" // + FmtHorizontalLine FormattingFeature = "horizontal_line" // hr + FmtHeaders FormattingFeature = "headers" // h1, h2, h3, h4, h5, h6 + FmtSuperscript FormattingFeature = "superscript" // sup + FmtSubscript FormattingFeature = "subscript" // sub + FmtMath FormattingFeature = "math" // + FmtDetailsSummary FormattingFeature = "details_summary" //
      ......
      + FmtTable FormattingFeature = "table" // table, thead, tbody, tr, th, td +) + +type FileFeatures struct { + // N.B. New fields need to be added to the Hash function to be included in the deduplication hash. + + MimeTypes map[string]CapabilitySupportLevel `json:"mime_types"` + + Caption CapabilitySupportLevel `json:"caption,omitempty"` + MaxCaptionLength int `json:"max_caption_length,omitempty"` + + MaxSize int64 `json:"max_size,omitempty"` + MaxWidth int `json:"max_width,omitempty"` + MaxHeight int `json:"max_height,omitempty"` + MaxDuration *jsontime.Seconds `json:"max_duration,omitempty"` + + ViewOnce bool `json:"view_once,omitempty"` +} + +func (ff *FileFeatures) GetMimeSupport(inputType string) CapabilitySupportLevel { + match, ok := ff.MimeTypes[inputType] + if ok { + return match + } + if strings.IndexByte(inputType, ';') != -1 { + plainMime, _, _ := mime.ParseMediaType(inputType) + if plainMime != "" { + if match, ok = ff.MimeTypes[plainMime]; ok { + return match + } + } + } + if slash := strings.IndexByte(inputType, '/'); slash > 0 { + generalType := fmt.Sprintf("%s/*", inputType[:slash]) + if match, ok = ff.MimeTypes[generalType]; ok { + return match + } + } + match, ok = ff.MimeTypes["*/*"] + if ok { + return match + } + return CapLevelRejected +} + +type hashable interface { + Hash() []byte +} + +func hashMap[Key ~string, Value hashable](w io.Writer, name string, data map[Key]Value) { + keys := maps.Keys(data) + slices.Sort(keys) + exerrors.Must(w.Write([]byte(name))) + for _, key := range keys { + exerrors.Must(w.Write([]byte(key))) + exerrors.Must(w.Write(data[key].Hash())) + exerrors.Must(w.Write([]byte{0})) + } +} + +func hashValue(w io.Writer, name string, data hashable) { + exerrors.Must(w.Write([]byte(name))) + exerrors.Must(w.Write(data.Hash())) +} + +func hashInt[T constraints.Integer](w io.Writer, name string, data T) { + exerrors.Must(w.Write(binary.BigEndian.AppendUint64([]byte(name), uint64(data)))) +} + +func hashBool[T ~bool](w io.Writer, name string, data T) { + exerrors.Must(w.Write([]byte(name))) + if data { + exerrors.Must(w.Write([]byte{1})) + } else { + exerrors.Must(w.Write([]byte{0})) + } +} + +func (csl CapabilitySupportLevel) Hash() []byte { + return []byte{byte(csl + 128)} +} + +func (rf *RoomFeatures) Hash() []byte { + hasher := sha256.New() + + hashMap(hasher, "formatting", rf.Formatting) + hashMap(hasher, "file", rf.File) + + hashInt(hasher, "max_text_length", rf.MaxTextLength) + + hashValue(hasher, "location_message", rf.LocationMessage) + hashValue(hasher, "poll", rf.Poll) + hashValue(hasher, "thread", rf.Thread) + hashValue(hasher, "reply", rf.Reply) + + hashValue(hasher, "edit", rf.Edit) + hashInt(hasher, "edit_max_count", rf.EditMaxCount) + hashInt(hasher, "edit_max_age", rf.EditMaxAge.Get()) + + hashValue(hasher, "delete", rf.Delete) + hashBool(hasher, "delete_for_me", rf.DeleteForMe) + hashInt(hasher, "delete_max_age", rf.DeleteMaxAge.Get()) + hashValue(hasher, "disappearing_timer", rf.DisappearingTimer) + + hashValue(hasher, "reaction", rf.Reaction) + hashInt(hasher, "reaction_count", rf.ReactionCount) + hasher.Write([]byte("allowed_reactions")) + for _, reaction := range rf.AllowedReactions { + hasher.Write([]byte(reaction)) + } + hashBool(hasher, "custom_emoji_reactions", rf.CustomEmojiReactions) + + hashBool(hasher, "read_receipts", rf.ReadReceipts) + hashBool(hasher, "typing_notifications", rf.TypingNotifications) + hashBool(hasher, "archive", rf.Archive) + hashBool(hasher, "mark_as_unread", rf.MarkAsUnread) + hashBool(hasher, "delete_chat", rf.DeleteChat) + hashBool(hasher, "delete_chat_for_everyone", rf.DeleteChatForEveryone) + + return hasher.Sum(nil) +} + +func (dtc *DisappearingTimerCapability) Hash() []byte { + if dtc == nil { + return nil + } + hasher := sha256.New() + hasher.Write([]byte("types")) + for _, t := range dtc.Types { + hasher.Write([]byte(t)) + } + hasher.Write([]byte("timers")) + for _, timer := range dtc.Timers { + hashInt(hasher, "", timer.Milliseconds()) + } + return hasher.Sum(nil) +} + +func (ff *FileFeatures) Hash() []byte { + hasher := sha256.New() + hashMap(hasher, "mime_types", ff.MimeTypes) + hashValue(hasher, "caption", ff.Caption) + hashInt(hasher, "max_caption_length", ff.MaxCaptionLength) + hashInt(hasher, "max_size", ff.MaxSize) + hashInt(hasher, "max_width", ff.MaxWidth) + hashInt(hasher, "max_height", ff.MaxHeight) + hashInt(hasher, "max_duration", ff.MaxDuration.Get()) + hashBool(hasher, "view_once", ff.ViewOnce) + return hasher.Sum(nil) +} + +func (ff *FileFeatures) Clone() *FileFeatures { + if ff == nil { + return nil + } + clone := *ff + clone.MimeTypes = maps.Clone(clone.MimeTypes) + clone.MaxDuration = ptr.Clone(clone.MaxDuration) + return &clone +} diff --git a/vendor/maunium.net/go/mautrix/event/content.go b/vendor/maunium.net/go/mautrix/event/content.go new file mode 100644 index 00000000..c0ff51ad --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/content.go @@ -0,0 +1,621 @@ +// Copyright (c) 2021 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/gob" + "encoding/json" + "errors" + "fmt" + "reflect" +) + +// TypeMap is a mapping from event type to the content struct type. +// This is used by Content.ParseRaw() for creating the correct type of struct. +var TypeMap = map[Type]reflect.Type{ + StateMember: reflect.TypeOf(MemberEventContent{}), + StateThirdPartyInvite: reflect.TypeOf(ThirdPartyInviteEventContent{}), + StatePowerLevels: reflect.TypeOf(PowerLevelsEventContent{}), + StateCanonicalAlias: reflect.TypeOf(CanonicalAliasEventContent{}), + StateRoomName: reflect.TypeOf(RoomNameEventContent{}), + StateRoomAvatar: reflect.TypeOf(RoomAvatarEventContent{}), + StateServerACL: reflect.TypeOf(ServerACLEventContent{}), + StateTopic: reflect.TypeOf(TopicEventContent{}), + StateTombstone: reflect.TypeOf(TombstoneEventContent{}), + StateCreate: reflect.TypeOf(CreateEventContent{}), + StateJoinRules: reflect.TypeOf(JoinRulesEventContent{}), + StateHistoryVisibility: reflect.TypeOf(HistoryVisibilityEventContent{}), + StateGuestAccess: reflect.TypeOf(GuestAccessEventContent{}), + StatePinnedEvents: reflect.TypeOf(PinnedEventsEventContent{}), + StatePolicyRoom: reflect.TypeOf(ModPolicyContent{}), + StatePolicyServer: reflect.TypeOf(ModPolicyContent{}), + StatePolicyUser: reflect.TypeOf(ModPolicyContent{}), + StateEncryption: reflect.TypeOf(EncryptionEventContent{}), + StateBridge: reflect.TypeOf(BridgeEventContent{}), + StateHalfShotBridge: reflect.TypeOf(BridgeEventContent{}), + StateSpaceParent: reflect.TypeOf(SpaceParentEventContent{}), + StateSpaceChild: reflect.TypeOf(SpaceChildEventContent{}), + + StateLegacyPolicyRoom: reflect.TypeOf(ModPolicyContent{}), + StateLegacyPolicyServer: reflect.TypeOf(ModPolicyContent{}), + StateLegacyPolicyUser: reflect.TypeOf(ModPolicyContent{}), + StateUnstablePolicyRoom: reflect.TypeOf(ModPolicyContent{}), + StateUnstablePolicyServer: reflect.TypeOf(ModPolicyContent{}), + StateUnstablePolicyUser: reflect.TypeOf(ModPolicyContent{}), + + StateElementFunctionalMembers: reflect.TypeOf(ElementFunctionalMembersContent{}), + StateBeeperRoomFeatures: reflect.TypeOf(RoomFeatures{}), + StateBeeperDisappearingTimer: reflect.TypeOf(BeeperDisappearingTimer{}), + StateBotCommands: reflect.TypeOf(BotCommandsEventContent{}), + + EventMessage: reflect.TypeOf(MessageEventContent{}), + EventSticker: reflect.TypeOf(MessageEventContent{}), + EventEncrypted: reflect.TypeOf(EncryptedEventContent{}), + EventRedaction: reflect.TypeOf(RedactionEventContent{}), + EventReaction: reflect.TypeOf(ReactionEventContent{}), + + EventUnstablePollStart: reflect.TypeOf(PollStartEventContent{}), + EventUnstablePollResponse: reflect.TypeOf(PollResponseEventContent{}), + + BeeperMessageStatus: reflect.TypeOf(BeeperMessageStatusEventContent{}), + BeeperTranscription: reflect.TypeOf(BeeperTranscriptionEventContent{}), + BeeperDeleteChat: reflect.TypeOf(BeeperChatDeleteEventContent{}), + + AccountDataRoomTags: reflect.TypeOf(TagEventContent{}), + AccountDataDirectChats: reflect.TypeOf(DirectChatsEventContent{}), + AccountDataFullyRead: reflect.TypeOf(FullyReadEventContent{}), + AccountDataIgnoredUserList: reflect.TypeOf(IgnoredUserListEventContent{}), + AccountDataMarkedUnread: reflect.TypeOf(MarkedUnreadEventContent{}), + AccountDataBeeperMute: reflect.TypeOf(BeeperMuteEventContent{}), + + EphemeralEventTyping: reflect.TypeOf(TypingEventContent{}), + EphemeralEventReceipt: reflect.TypeOf(ReceiptEventContent{}), + EphemeralEventPresence: reflect.TypeOf(PresenceEventContent{}), + + InRoomVerificationReady: reflect.TypeOf(VerificationReadyEventContent{}), + InRoomVerificationStart: reflect.TypeOf(VerificationStartEventContent{}), + InRoomVerificationDone: reflect.TypeOf(VerificationDoneEventContent{}), + InRoomVerificationCancel: reflect.TypeOf(VerificationCancelEventContent{}), + + InRoomVerificationAccept: reflect.TypeOf(VerificationAcceptEventContent{}), + InRoomVerificationKey: reflect.TypeOf(VerificationKeyEventContent{}), + InRoomVerificationMAC: reflect.TypeOf(VerificationMACEventContent{}), + + ToDeviceRoomKey: reflect.TypeOf(RoomKeyEventContent{}), + ToDeviceForwardedRoomKey: reflect.TypeOf(ForwardedRoomKeyEventContent{}), + ToDeviceRoomKeyRequest: reflect.TypeOf(RoomKeyRequestEventContent{}), + ToDeviceEncrypted: reflect.TypeOf(EncryptedEventContent{}), + ToDeviceRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}), + ToDeviceSecretRequest: reflect.TypeOf(SecretRequestEventContent{}), + ToDeviceSecretSend: reflect.TypeOf(SecretSendEventContent{}), + ToDeviceDummy: reflect.TypeOf(DummyEventContent{}), + + ToDeviceVerificationRequest: reflect.TypeOf(VerificationRequestEventContent{}), + ToDeviceVerificationReady: reflect.TypeOf(VerificationReadyEventContent{}), + ToDeviceVerificationStart: reflect.TypeOf(VerificationStartEventContent{}), + ToDeviceVerificationDone: reflect.TypeOf(VerificationDoneEventContent{}), + ToDeviceVerificationCancel: reflect.TypeOf(VerificationCancelEventContent{}), + + ToDeviceVerificationAccept: reflect.TypeOf(VerificationAcceptEventContent{}), + ToDeviceVerificationKey: reflect.TypeOf(VerificationKeyEventContent{}), + ToDeviceVerificationMAC: reflect.TypeOf(VerificationMACEventContent{}), + + ToDeviceOrgMatrixRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}), + + ToDeviceBeeperRoomKeyAck: reflect.TypeOf(BeeperRoomKeyAckEventContent{}), + + CallInvite: reflect.TypeOf(CallInviteEventContent{}), + CallCandidates: reflect.TypeOf(CallCandidatesEventContent{}), + CallAnswer: reflect.TypeOf(CallAnswerEventContent{}), + CallReject: reflect.TypeOf(CallRejectEventContent{}), + CallSelectAnswer: reflect.TypeOf(CallSelectAnswerEventContent{}), + CallNegotiate: reflect.TypeOf(CallNegotiateEventContent{}), + CallHangup: reflect.TypeOf(CallHangupEventContent{}), +} + +// Content stores the content of a Matrix event. +// +// By default, the raw JSON bytes are stored in VeryRaw and parsed into a map[string]interface{} in the Raw field. +// Additionally, you can call ParseRaw with the correct event type to parse the (VeryRaw) content into a nicer struct, +// which you can then access from Parsed or via the helper functions. +// +// When being marshaled into JSON, the data in Parsed will be marshaled first and then recursively merged +// with the data in Raw. Values in Raw are preferred, but nested objects will be recursed into before merging, +// rather than overriding the whole object with the one in Raw). +// If one of them is nil, then only the other is used. If both (Parsed and Raw) are nil, VeryRaw is used instead. +type Content struct { + VeryRaw json.RawMessage + Raw map[string]interface{} + Parsed interface{} +} + +type Relatable interface { + GetRelatesTo() *RelatesTo + OptionalGetRelatesTo() *RelatesTo + SetRelatesTo(rel *RelatesTo) +} + +func (content *Content) UnmarshalJSON(data []byte) error { + content.VeryRaw = data + err := json.Unmarshal(data, &content.Raw) + return err +} + +func (content *Content) MarshalJSON() ([]byte, error) { + if content.Raw == nil { + if content.Parsed == nil { + if content.VeryRaw == nil { + return []byte("{}"), nil + } + return content.VeryRaw, nil + } + return json.Marshal(content.Parsed) + } else if content.Parsed != nil { + // TODO this whole thing is incredibly hacky + // It needs to produce JSON, where: + // * content.Parsed is applied after content.Raw + // * MarshalJSON() is respected inside content.Parsed + // * Custom field inside nested objects of content.Raw are preserved, + // even if content.Parsed contains the higher-level objects. + // * content.Raw is not modified + + unparsed, err := json.Marshal(content.Parsed) + if err != nil { + return nil, err + } + + var rawParsed map[string]interface{} + err = json.Unmarshal(unparsed, &rawParsed) + if err != nil { + return nil, err + } + + output := make(map[string]interface{}) + for key, value := range content.Raw { + output[key] = value + } + + mergeMaps(output, rawParsed) + return json.Marshal(output) + } + return json.Marshal(content.Raw) +} + +// Deprecated: use errors.Is directly +func IsUnsupportedContentType(err error) bool { + return errors.Is(err, ErrUnsupportedContentType) +} + +var ErrContentAlreadyParsed = errors.New("content is already parsed") +var ErrUnsupportedContentType = errors.New("unsupported event type") + +func (content *Content) GetRaw() map[string]interface{} { + if content.Raw == nil { + content.Raw = make(map[string]interface{}) + } + return content.Raw +} + +func (content *Content) ParseRaw(evtType Type) error { + if content.Parsed != nil { + return ErrContentAlreadyParsed + } + structType, ok := TypeMap[evtType] + if !ok { + return fmt.Errorf("%w %s", ErrUnsupportedContentType, evtType.Repr()) + } + content.Parsed = reflect.New(structType).Interface() + return json.Unmarshal(content.VeryRaw, &content.Parsed) +} + +func mergeMaps(into, from map[string]interface{}) { + for key, newValue := range from { + existingValue, ok := into[key] + if !ok { + into[key] = newValue + continue + } + existingValueMap, okEx := existingValue.(map[string]interface{}) + newValueMap, okNew := newValue.(map[string]interface{}) + if okEx && okNew { + mergeMaps(existingValueMap, newValueMap) + } else { + into[key] = newValue + } + } +} + +func init() { + gob.Register(&MemberEventContent{}) + gob.Register(&PowerLevelsEventContent{}) + gob.Register(&CanonicalAliasEventContent{}) + gob.Register(&EncryptionEventContent{}) + gob.Register(&BridgeEventContent{}) + gob.Register(&SpaceChildEventContent{}) + gob.Register(&SpaceParentEventContent{}) + gob.Register(&ElementFunctionalMembersContent{}) + gob.Register(&RoomNameEventContent{}) + gob.Register(&RoomAvatarEventContent{}) + gob.Register(&TopicEventContent{}) + gob.Register(&TombstoneEventContent{}) + gob.Register(&CreateEventContent{}) + gob.Register(&JoinRulesEventContent{}) + gob.Register(&HistoryVisibilityEventContent{}) + gob.Register(&GuestAccessEventContent{}) + gob.Register(&PinnedEventsEventContent{}) + gob.Register(&MessageEventContent{}) + gob.Register(&MessageEventContent{}) + gob.Register(&EncryptedEventContent{}) + gob.Register(&RedactionEventContent{}) + gob.Register(&ReactionEventContent{}) + gob.Register(&TagEventContent{}) + gob.Register(&DirectChatsEventContent{}) + gob.Register(&FullyReadEventContent{}) + gob.Register(&IgnoredUserListEventContent{}) + gob.Register(&TypingEventContent{}) + gob.Register(&ReceiptEventContent{}) + gob.Register(&PresenceEventContent{}) + gob.Register(&RoomKeyEventContent{}) + gob.Register(&ForwardedRoomKeyEventContent{}) + gob.Register(&RoomKeyRequestEventContent{}) + gob.Register(&RoomKeyWithheldEventContent{}) +} + +func CastOrDefault[T any](content *Content) *T { + casted, ok := content.Parsed.(*T) + if ok { + return casted + } + casted2, _ := content.Parsed.(T) + return &casted2 +} + +// Helper cast functions below + +func (content *Content) AsMember() *MemberEventContent { + casted, ok := content.Parsed.(*MemberEventContent) + if !ok { + return &MemberEventContent{} + } + return casted +} +func (content *Content) AsPowerLevels() *PowerLevelsEventContent { + casted, ok := content.Parsed.(*PowerLevelsEventContent) + if !ok { + return &PowerLevelsEventContent{} + } + return casted +} +func (content *Content) AsCanonicalAlias() *CanonicalAliasEventContent { + casted, ok := content.Parsed.(*CanonicalAliasEventContent) + if !ok { + return &CanonicalAliasEventContent{} + } + return casted +} +func (content *Content) AsRoomName() *RoomNameEventContent { + casted, ok := content.Parsed.(*RoomNameEventContent) + if !ok { + return &RoomNameEventContent{} + } + return casted +} +func (content *Content) AsRoomAvatar() *RoomAvatarEventContent { + casted, ok := content.Parsed.(*RoomAvatarEventContent) + if !ok { + return &RoomAvatarEventContent{} + } + return casted +} +func (content *Content) AsTopic() *TopicEventContent { + casted, ok := content.Parsed.(*TopicEventContent) + if !ok { + return &TopicEventContent{} + } + return casted +} +func (content *Content) AsTombstone() *TombstoneEventContent { + casted, ok := content.Parsed.(*TombstoneEventContent) + if !ok { + return &TombstoneEventContent{} + } + return casted +} +func (content *Content) AsCreate() *CreateEventContent { + casted, ok := content.Parsed.(*CreateEventContent) + if !ok { + return &CreateEventContent{} + } + return casted +} +func (content *Content) AsJoinRules() *JoinRulesEventContent { + casted, ok := content.Parsed.(*JoinRulesEventContent) + if !ok { + return &JoinRulesEventContent{} + } + return casted +} +func (content *Content) AsHistoryVisibility() *HistoryVisibilityEventContent { + casted, ok := content.Parsed.(*HistoryVisibilityEventContent) + if !ok { + return &HistoryVisibilityEventContent{} + } + return casted +} +func (content *Content) AsGuestAccess() *GuestAccessEventContent { + casted, ok := content.Parsed.(*GuestAccessEventContent) + if !ok { + return &GuestAccessEventContent{} + } + return casted +} +func (content *Content) AsPinnedEvents() *PinnedEventsEventContent { + casted, ok := content.Parsed.(*PinnedEventsEventContent) + if !ok { + return &PinnedEventsEventContent{} + } + return casted +} +func (content *Content) AsEncryption() *EncryptionEventContent { + casted, ok := content.Parsed.(*EncryptionEventContent) + if !ok { + return &EncryptionEventContent{} + } + return casted +} +func (content *Content) AsBridge() *BridgeEventContent { + casted, ok := content.Parsed.(*BridgeEventContent) + if !ok { + return &BridgeEventContent{} + } + return casted +} +func (content *Content) AsSpaceChild() *SpaceChildEventContent { + casted, ok := content.Parsed.(*SpaceChildEventContent) + if !ok { + return &SpaceChildEventContent{} + } + return casted +} +func (content *Content) AsSpaceParent() *SpaceParentEventContent { + casted, ok := content.Parsed.(*SpaceParentEventContent) + if !ok { + return &SpaceParentEventContent{} + } + return casted +} +func (content *Content) AsElementFunctionalMembers() *ElementFunctionalMembersContent { + casted, ok := content.Parsed.(*ElementFunctionalMembersContent) + if !ok { + return &ElementFunctionalMembersContent{} + } + return casted +} +func (content *Content) AsMessage() *MessageEventContent { + casted, ok := content.Parsed.(*MessageEventContent) + if !ok { + return &MessageEventContent{} + } + return casted +} +func (content *Content) AsEncrypted() *EncryptedEventContent { + casted, ok := content.Parsed.(*EncryptedEventContent) + if !ok { + return &EncryptedEventContent{} + } + return casted +} +func (content *Content) AsRedaction() *RedactionEventContent { + casted, ok := content.Parsed.(*RedactionEventContent) + if !ok { + return &RedactionEventContent{} + } + return casted +} +func (content *Content) AsReaction() *ReactionEventContent { + casted, ok := content.Parsed.(*ReactionEventContent) + if !ok { + return &ReactionEventContent{} + } + return casted +} +func (content *Content) AsTag() *TagEventContent { + casted, ok := content.Parsed.(*TagEventContent) + if !ok { + return &TagEventContent{} + } + return casted +} +func (content *Content) AsDirectChats() *DirectChatsEventContent { + casted, ok := content.Parsed.(*DirectChatsEventContent) + if !ok { + return &DirectChatsEventContent{} + } + return casted +} +func (content *Content) AsFullyRead() *FullyReadEventContent { + casted, ok := content.Parsed.(*FullyReadEventContent) + if !ok { + return &FullyReadEventContent{} + } + return casted +} +func (content *Content) AsIgnoredUserList() *IgnoredUserListEventContent { + casted, ok := content.Parsed.(*IgnoredUserListEventContent) + if !ok { + return &IgnoredUserListEventContent{} + } + return casted +} +func (content *Content) AsMarkedUnread() *MarkedUnreadEventContent { + casted, ok := content.Parsed.(*MarkedUnreadEventContent) + if !ok { + return &MarkedUnreadEventContent{} + } + return casted +} +func (content *Content) AsTyping() *TypingEventContent { + casted, ok := content.Parsed.(*TypingEventContent) + if !ok { + return &TypingEventContent{} + } + return casted +} +func (content *Content) AsReceipt() *ReceiptEventContent { + casted, ok := content.Parsed.(*ReceiptEventContent) + if !ok { + return &ReceiptEventContent{} + } + return casted +} +func (content *Content) AsPresence() *PresenceEventContent { + casted, ok := content.Parsed.(*PresenceEventContent) + if !ok { + return &PresenceEventContent{} + } + return casted +} +func (content *Content) AsRoomKey() *RoomKeyEventContent { + casted, ok := content.Parsed.(*RoomKeyEventContent) + if !ok { + return &RoomKeyEventContent{} + } + return casted +} +func (content *Content) AsForwardedRoomKey() *ForwardedRoomKeyEventContent { + casted, ok := content.Parsed.(*ForwardedRoomKeyEventContent) + if !ok { + return &ForwardedRoomKeyEventContent{} + } + return casted +} +func (content *Content) AsRoomKeyRequest() *RoomKeyRequestEventContent { + casted, ok := content.Parsed.(*RoomKeyRequestEventContent) + if !ok { + return &RoomKeyRequestEventContent{} + } + return casted +} +func (content *Content) AsRoomKeyWithheld() *RoomKeyWithheldEventContent { + casted, ok := content.Parsed.(*RoomKeyWithheldEventContent) + if !ok { + return &RoomKeyWithheldEventContent{} + } + return casted +} +func (content *Content) AsCallInvite() *CallInviteEventContent { + casted, ok := content.Parsed.(*CallInviteEventContent) + if !ok { + return &CallInviteEventContent{} + } + return casted +} +func (content *Content) AsCallCandidates() *CallCandidatesEventContent { + casted, ok := content.Parsed.(*CallCandidatesEventContent) + if !ok { + return &CallCandidatesEventContent{} + } + return casted +} +func (content *Content) AsCallAnswer() *CallAnswerEventContent { + casted, ok := content.Parsed.(*CallAnswerEventContent) + if !ok { + return &CallAnswerEventContent{} + } + return casted +} +func (content *Content) AsCallReject() *CallRejectEventContent { + casted, ok := content.Parsed.(*CallRejectEventContent) + if !ok { + return &CallRejectEventContent{} + } + return casted +} +func (content *Content) AsCallSelectAnswer() *CallSelectAnswerEventContent { + casted, ok := content.Parsed.(*CallSelectAnswerEventContent) + if !ok { + return &CallSelectAnswerEventContent{} + } + return casted +} +func (content *Content) AsCallNegotiate() *CallNegotiateEventContent { + casted, ok := content.Parsed.(*CallNegotiateEventContent) + if !ok { + return &CallNegotiateEventContent{} + } + return casted +} +func (content *Content) AsCallHangup() *CallHangupEventContent { + casted, ok := content.Parsed.(*CallHangupEventContent) + if !ok { + return &CallHangupEventContent{} + } + return casted +} +func (content *Content) AsModPolicy() *ModPolicyContent { + casted, ok := content.Parsed.(*ModPolicyContent) + if !ok { + return &ModPolicyContent{} + } + return casted +} +func (content *Content) AsVerificationRequest() *VerificationRequestEventContent { + casted, ok := content.Parsed.(*VerificationRequestEventContent) + if !ok { + return &VerificationRequestEventContent{} + } + return casted +} +func (content *Content) AsVerificationReady() *VerificationReadyEventContent { + casted, ok := content.Parsed.(*VerificationReadyEventContent) + if !ok { + return &VerificationReadyEventContent{} + } + return casted +} +func (content *Content) AsVerificationStart() *VerificationStartEventContent { + casted, ok := content.Parsed.(*VerificationStartEventContent) + if !ok { + return &VerificationStartEventContent{} + } + return casted +} +func (content *Content) AsVerificationDone() *VerificationDoneEventContent { + casted, ok := content.Parsed.(*VerificationDoneEventContent) + if !ok { + return &VerificationDoneEventContent{} + } + return casted +} +func (content *Content) AsVerificationCancel() *VerificationCancelEventContent { + casted, ok := content.Parsed.(*VerificationCancelEventContent) + if !ok { + return &VerificationCancelEventContent{} + } + return casted +} +func (content *Content) AsVerificationAccept() *VerificationAcceptEventContent { + casted, ok := content.Parsed.(*VerificationAcceptEventContent) + if !ok { + return &VerificationAcceptEventContent{} + } + return casted +} +func (content *Content) AsVerificationKey() *VerificationKeyEventContent { + casted, ok := content.Parsed.(*VerificationKeyEventContent) + if !ok { + return &VerificationKeyEventContent{} + } + return casted +} +func (content *Content) AsVerificationMAC() *VerificationMACEventContent { + casted, ok := content.Parsed.(*VerificationMACEventContent) + if !ok { + return &VerificationMACEventContent{} + } + return casted +} diff --git a/vendor/maunium.net/go/mautrix/event/delayed.go b/vendor/maunium.net/go/mautrix/event/delayed.go new file mode 100644 index 00000000..fefb62af --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/delayed.go @@ -0,0 +1,70 @@ +package event + +import ( + "encoding/json" + + "go.mau.fi/util/jsontime" + + "maunium.net/go/mautrix/id" +) + +type ScheduledDelayedEvent struct { + DelayID id.DelayID `json:"delay_id"` + RoomID id.RoomID `json:"room_id"` + Type Type `json:"type"` + StateKey *string `json:"state_key,omitempty"` + Delay int64 `json:"delay"` + RunningSince jsontime.UnixMilli `json:"running_since"` + Content Content `json:"content"` +} + +func (e ScheduledDelayedEvent) AsEvent(eventID id.EventID, ts jsontime.UnixMilli) (*Event, error) { + evt := &Event{ + ID: eventID, + RoomID: e.RoomID, + Type: e.Type, + StateKey: e.StateKey, + Content: e.Content, + Timestamp: ts.UnixMilli(), + } + return evt, evt.Content.ParseRaw(evt.Type) +} + +type FinalisedDelayedEvent struct { + DelayedEvent *ScheduledDelayedEvent `json:"scheduled_event"` + Outcome DelayOutcome `json:"outcome"` + Reason DelayReason `json:"reason"` + Error json.RawMessage `json:"error,omitempty"` + EventID id.EventID `json:"event_id,omitempty"` + Timestamp jsontime.UnixMilli `json:"origin_server_ts"` +} + +type DelayStatus string + +var ( + DelayStatusScheduled DelayStatus = "scheduled" + DelayStatusFinalised DelayStatus = "finalised" +) + +type DelayAction string + +var ( + DelayActionSend DelayAction = "send" + DelayActionCancel DelayAction = "cancel" + DelayActionRestart DelayAction = "restart" +) + +type DelayOutcome string + +var ( + DelayOutcomeSend DelayOutcome = "send" + DelayOutcomeCancel DelayOutcome = "cancel" +) + +type DelayReason string + +var ( + DelayReasonAction DelayReason = "action" + DelayReasonError DelayReason = "error" + DelayReasonDelay DelayReason = "delay" +) diff --git a/vendor/maunium.net/go/mautrix/event/encryption.go b/vendor/maunium.net/go/mautrix/event/encryption.go new file mode 100644 index 00000000..cf9c2814 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/encryption.go @@ -0,0 +1,202 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "fmt" + + "maunium.net/go/mautrix/id" +) + +// EncryptionEventContent represents the content of a m.room.encryption state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomencryption +type EncryptionEventContent struct { + // The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'. + Algorithm id.Algorithm `json:"algorithm"` + // How long the session should be used before changing it. 604800000 (a week) is the recommended default. + RotationPeriodMillis int64 `json:"rotation_period_ms,omitempty"` + // How many messages should be sent before changing the session. 100 is the recommended default. + RotationPeriodMessages int `json:"rotation_period_msgs,omitempty"` +} + +// EncryptedEventContent represents the content of a m.room.encrypted message event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomencrypted +// +// Note that sender_key and device_id are deprecated in Megolm events as of https://github.com/matrix-org/matrix-spec-proposals/pull/3700 +type EncryptedEventContent struct { + Algorithm id.Algorithm `json:"algorithm"` + SenderKey id.SenderKey `json:"sender_key,omitempty"` + // Deprecated: Matrix v1.3 + DeviceID id.DeviceID `json:"device_id,omitempty"` + // Only present for Megolm events + SessionID id.SessionID `json:"session_id,omitempty"` + + Ciphertext json.RawMessage `json:"ciphertext"` + + MegolmCiphertext []byte `json:"-"` + OlmCiphertext OlmCiphertexts `json:"-"` + + RelatesTo *RelatesTo `json:"m.relates_to,omitempty"` + Mentions *Mentions `json:"m.mentions,omitempty"` +} + +type OlmCiphertexts map[id.Curve25519]struct { + Body string `json:"body"` + Type id.OlmMsgType `json:"type"` +} + +type serializableEncryptedEventContent EncryptedEventContent + +func (content *EncryptedEventContent) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, (*serializableEncryptedEventContent)(content)) + if err != nil { + return err + } + switch content.Algorithm { + case id.AlgorithmOlmV1: + content.OlmCiphertext = make(OlmCiphertexts) + return json.Unmarshal(content.Ciphertext, &content.OlmCiphertext) + case id.AlgorithmMegolmV1: + if len(content.Ciphertext) == 0 || content.Ciphertext[0] != '"' || content.Ciphertext[len(content.Ciphertext)-1] != '"' { + return id.InputNotJSONString + } + content.MegolmCiphertext = content.Ciphertext[1 : len(content.Ciphertext)-1] + } + return nil +} + +func (content *EncryptedEventContent) MarshalJSON() ([]byte, error) { + var err error + switch content.Algorithm { + case id.AlgorithmOlmV1: + content.Ciphertext, err = json.Marshal(content.OlmCiphertext) + case id.AlgorithmMegolmV1: + content.Ciphertext = make([]byte, len(content.MegolmCiphertext)+2) + content.Ciphertext[0] = '"' + content.Ciphertext[len(content.Ciphertext)-1] = '"' + copy(content.Ciphertext[1:len(content.Ciphertext)-1], content.MegolmCiphertext) + } + if err != nil { + return nil, err + } + return json.Marshal((*serializableEncryptedEventContent)(content)) +} + +// RoomKeyEventContent represents the content of a m.room_key to_device event. +// https://spec.matrix.org/v1.2/client-server-api/#mroom_key +type RoomKeyEventContent struct { + Algorithm id.Algorithm `json:"algorithm"` + RoomID id.RoomID `json:"room_id"` + SessionID id.SessionID `json:"session_id"` + SessionKey string `json:"session_key"` + + MaxAge int64 `json:"com.beeper.max_age_ms"` + MaxMessages int `json:"com.beeper.max_messages"` + IsScheduled bool `json:"com.beeper.is_scheduled"` +} + +// ForwardedRoomKeyEventContent represents the content of a m.forwarded_room_key to_device event. +// https://spec.matrix.org/v1.2/client-server-api/#mforwarded_room_key +type ForwardedRoomKeyEventContent struct { + RoomKeyEventContent + SenderKey id.SenderKey `json:"sender_key"` + SenderClaimedKey id.Ed25519 `json:"sender_claimed_ed25519_key"` + ForwardingKeyChain []string `json:"forwarding_curve25519_key_chain"` + + MaxAge int64 `json:"com.beeper.max_age_ms"` + MaxMessages int `json:"com.beeper.max_messages"` + IsScheduled bool `json:"com.beeper.is_scheduled"` +} + +type KeyRequestAction string + +const ( + KeyRequestActionRequest = "request" + KeyRequestActionCancel = "request_cancellation" +) + +// RoomKeyRequestEventContent represents the content of a m.room_key_request to_device event. +// https://spec.matrix.org/v1.2/client-server-api/#mroom_key_request +type RoomKeyRequestEventContent struct { + Body RequestedKeyInfo `json:"body"` + Action KeyRequestAction `json:"action"` + RequestingDeviceID id.DeviceID `json:"requesting_device_id"` + RequestID string `json:"request_id"` +} + +type RequestedKeyInfo struct { + Algorithm id.Algorithm `json:"algorithm"` + RoomID id.RoomID `json:"room_id"` + SenderKey id.SenderKey `json:"sender_key"` + SessionID id.SessionID `json:"session_id"` +} + +type RoomKeyWithheldCode string + +const ( + RoomKeyWithheldBlacklisted RoomKeyWithheldCode = "m.blacklisted" + RoomKeyWithheldUnverified RoomKeyWithheldCode = "m.unverified" + RoomKeyWithheldUnauthorized RoomKeyWithheldCode = "m.unauthorised" + RoomKeyWithheldUnavailable RoomKeyWithheldCode = "m.unavailable" + RoomKeyWithheldNoOlmSession RoomKeyWithheldCode = "m.no_olm" + + RoomKeyWithheldBeeperRedacted RoomKeyWithheldCode = "com.beeper.redacted" +) + +type RoomKeyWithheldEventContent struct { + RoomID id.RoomID `json:"room_id,omitempty"` + Algorithm id.Algorithm `json:"algorithm"` + SessionID id.SessionID `json:"session_id,omitempty"` + SenderKey id.SenderKey `json:"sender_key"` + Code RoomKeyWithheldCode `json:"code"` + Reason string `json:"reason,omitempty"` +} + +const groupSessionWithheldMsg = "group session has been withheld: %s" + +func (withheld *RoomKeyWithheldEventContent) Error() string { + switch withheld.Code { + case RoomKeyWithheldBlacklisted, RoomKeyWithheldUnverified, RoomKeyWithheldUnauthorized, RoomKeyWithheldUnavailable, RoomKeyWithheldNoOlmSession: + return fmt.Sprintf(groupSessionWithheldMsg, withheld.Code) + default: + return fmt.Sprintf(groupSessionWithheldMsg+" (%s)", withheld.Code, withheld.Reason) + } +} + +func (withheld *RoomKeyWithheldEventContent) Is(other error) bool { + otherWithheld, ok := other.(*RoomKeyWithheldEventContent) + if !ok { + return false + } + return withheld.Code == "" || otherWithheld.Code == "" || withheld.Code == otherWithheld.Code +} + +type SecretRequestAction string + +func (a SecretRequestAction) String() string { + return string(a) +} + +const ( + SecretRequestRequest = "request" + SecretRequestCancellation = "request_cancellation" +) + +type SecretRequestEventContent struct { + Name id.Secret `json:"name,omitempty"` + Action SecretRequestAction `json:"action"` + RequestingDeviceID id.DeviceID `json:"requesting_device_id"` + RequestID string `json:"request_id"` +} + +type SecretSendEventContent struct { + RequestID string `json:"request_id"` + Secret string `json:"secret"` +} + +type DummyEventContent struct{} diff --git a/vendor/maunium.net/go/mautrix/event/ephemeral.go b/vendor/maunium.net/go/mautrix/event/ephemeral.go new file mode 100644 index 00000000..f447404b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/ephemeral.go @@ -0,0 +1,140 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "time" + + "maunium.net/go/mautrix/id" +) + +// TypingEventContent represents the content of a m.typing ephemeral event. +// https://spec.matrix.org/v1.2/client-server-api/#mtyping +type TypingEventContent struct { + UserIDs []id.UserID `json:"user_ids"` +} + +// ReceiptEventContent represents the content of a m.receipt ephemeral event. +// https://spec.matrix.org/v1.2/client-server-api/#mreceipt +type ReceiptEventContent map[id.EventID]Receipts + +func (rec ReceiptEventContent) Set(evtID id.EventID, receiptType ReceiptType, userID id.UserID, receipt ReadReceipt) { + rec.GetOrCreate(evtID).GetOrCreate(receiptType).Set(userID, receipt) +} + +func (rec ReceiptEventContent) GetOrCreate(evt id.EventID) Receipts { + receipts, ok := rec[evt] + if !ok { + receipts = make(Receipts) + rec[evt] = receipts + } + return receipts +} + +type ReceiptType string + +const ( + ReceiptTypeRead ReceiptType = "m.read" + ReceiptTypeReadPrivate ReceiptType = "m.read.private" +) + +type Receipts map[ReceiptType]UserReceipts + +func (rps Receipts) GetOrCreate(receiptType ReceiptType) UserReceipts { + read, ok := rps[receiptType] + if !ok { + read = make(UserReceipts) + rps[receiptType] = read + } + return read +} + +type UserReceipts map[id.UserID]ReadReceipt + +func (ur UserReceipts) Set(userID id.UserID, receipt ReadReceipt) { + ur[userID] = receipt +} + +type ThreadID = id.EventID + +const ReadReceiptThreadMain ThreadID = "main" + +type ReadReceipt struct { + Timestamp time.Time + + // Thread ID for thread-specific read receipts from MSC3771 + ThreadID ThreadID + + // Extra contains any unknown fields in the read receipt event. + // Most servers don't allow clients to set them, so this will be empty in most cases. + Extra map[string]interface{} +} + +func (rr *ReadReceipt) UnmarshalJSON(data []byte) error { + // Hacky compatibility hack against crappy clients that send double-encoded read receipts. + // TODO is this actually needed? clients can't currently set custom content in receipts 🤔 + if data[0] == '"' && data[len(data)-1] == '"' { + var strData string + err := json.Unmarshal(data, &strData) + if err != nil { + return err + } + data = []byte(strData) + } + + var parsed map[string]interface{} + err := json.Unmarshal(data, &parsed) + if err != nil { + return err + } + threadID, _ := parsed["thread_id"].(string) + ts, tsOK := parsed["ts"].(float64) + delete(parsed, "thread_id") + delete(parsed, "ts") + *rr = ReadReceipt{ + ThreadID: ThreadID(threadID), + Extra: parsed, + } + if tsOK { + rr.Timestamp = time.UnixMilli(int64(ts)) + } + return nil +} + +func (rr ReadReceipt) MarshalJSON() ([]byte, error) { + data := rr.Extra + if data == nil { + data = make(map[string]interface{}) + } + if rr.ThreadID != "" { + data["thread_id"] = rr.ThreadID + } + if !rr.Timestamp.IsZero() { + data["ts"] = rr.Timestamp.UnixMilli() + } + return json.Marshal(data) +} + +type Presence string + +const ( + PresenceOnline Presence = "online" + PresenceOffline Presence = "offline" + PresenceUnavailable Presence = "unavailable" +) + +// PresenceEventContent represents the content of a m.presence ephemeral event. +// https://spec.matrix.org/v1.2/client-server-api/#mpresence +type PresenceEventContent struct { + Presence Presence `json:"presence"` + Displayname string `json:"displayname,omitempty"` + AvatarURL id.ContentURIString `json:"avatar_url,omitempty"` + LastActiveAgo int64 `json:"last_active_ago,omitempty"` + CurrentlyActive bool `json:"currently_active,omitempty"` + StatusMessage string `json:"status_msg,omitempty"` +} diff --git a/vendor/maunium.net/go/mautrix/event/events.go b/vendor/maunium.net/go/mautrix/event/events.go new file mode 100644 index 00000000..72c1e161 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/events.go @@ -0,0 +1,158 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "time" + + "maunium.net/go/mautrix/id" +) + +// Event represents a single Matrix event. +type Event struct { + StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. + Sender id.UserID `json:"sender,omitempty"` // The user ID of the sender of the event + Type Type `json:"type"` // The event type + Timestamp int64 `json:"origin_server_ts,omitempty"` // The unix timestamp when this message was sent by the origin server + ID id.EventID `json:"event_id,omitempty"` // The unique ID of this event + RoomID id.RoomID `json:"room_id,omitempty"` // The room the event was sent to. May be nil (e.g. for presence) + Content Content `json:"content"` // The JSON content of the event. + Redacts id.EventID `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event + Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver. + + Mautrix MautrixInfo `json:"-"` + + ToUserID id.UserID `json:"to_user_id,omitempty"` // The user ID that the to-device event was sent to. Only present in MSC2409 appservice transactions. + ToDeviceID id.DeviceID `json:"to_device_id,omitempty"` // The device ID that the to-device event was sent to. Only present in MSC2409 appservice transactions. +} + +type eventForMarshaling struct { + StateKey *string `json:"state_key,omitempty"` + Sender id.UserID `json:"sender,omitempty"` + Type Type `json:"type"` + Timestamp int64 `json:"origin_server_ts,omitempty"` + ID id.EventID `json:"event_id,omitempty"` + RoomID id.RoomID `json:"room_id,omitempty"` + Content Content `json:"content"` + Redacts id.EventID `json:"redacts,omitempty"` + Unsigned *Unsigned `json:"unsigned,omitempty"` + + PrevContent *Content `json:"prev_content,omitempty"` + ReplacesState *id.EventID `json:"replaces_state,omitempty"` + + ToUserID id.UserID `json:"to_user_id,omitempty"` + ToDeviceID id.DeviceID `json:"to_device_id,omitempty"` +} + +// UnmarshalJSON unmarshals the event, including moving prev_content from the top level to inside unsigned. +func (evt *Event) UnmarshalJSON(data []byte) error { + var efm eventForMarshaling + err := json.Unmarshal(data, &efm) + if err != nil { + return err + } + evt.StateKey = efm.StateKey + evt.Sender = efm.Sender + evt.Type = efm.Type + evt.Timestamp = efm.Timestamp + evt.ID = efm.ID + evt.RoomID = efm.RoomID + evt.Content = efm.Content + evt.Redacts = efm.Redacts + if efm.Unsigned != nil { + evt.Unsigned = *efm.Unsigned + } + if efm.PrevContent != nil && evt.Unsigned.PrevContent == nil { + evt.Unsigned.PrevContent = efm.PrevContent + } + if efm.ReplacesState != nil && *efm.ReplacesState != "" && evt.Unsigned.ReplacesState == "" { + evt.Unsigned.ReplacesState = *efm.ReplacesState + } + evt.ToUserID = efm.ToUserID + evt.ToDeviceID = efm.ToDeviceID + return nil +} + +// MarshalJSON marshals the event, including omitting the unsigned field if it's empty. +// +// This is necessary because Unsigned is not a pointer (for convenience reasons), +// and encoding/json doesn't know how to check if a non-pointer struct is empty. +// +// TODO(tulir): maybe it makes more sense to make Unsigned a pointer and make an easy and safe way to access it? +func (evt *Event) MarshalJSON() ([]byte, error) { + unsigned := &evt.Unsigned + if unsigned.IsEmpty() { + unsigned = nil + } + return json.Marshal(&eventForMarshaling{ + StateKey: evt.StateKey, + Sender: evt.Sender, + Type: evt.Type, + Timestamp: evt.Timestamp, + ID: evt.ID, + RoomID: evt.RoomID, + Content: evt.Content, + Redacts: evt.Redacts, + Unsigned: unsigned, + ToUserID: evt.ToUserID, + ToDeviceID: evt.ToDeviceID, + }) +} + +type MautrixInfo struct { + EventSource Source + + TrustState id.TrustState + ForwardedKeys bool + WasEncrypted bool + TrustSource *id.Device + + ReceivedAt time.Time + EditedAt time.Time + LastEditID id.EventID + DecryptionDuration time.Duration + + CheckpointSent bool + // When using MSC4222 and the state_after field, this field is set + // for timeline events to indicate they shouldn't update room state. + IgnoreState bool +} + +func (evt *Event) GetStateKey() string { + if evt.StateKey != nil { + return *evt.StateKey + } + return "" +} + +type Unsigned struct { + PrevContent *Content `json:"prev_content,omitempty"` + PrevSender id.UserID `json:"prev_sender,omitempty"` + Membership Membership `json:"membership,omitempty"` + ReplacesState id.EventID `json:"replaces_state,omitempty"` + Age int64 `json:"age,omitempty"` + TransactionID string `json:"transaction_id,omitempty"` + Relations *Relations `json:"m.relations,omitempty"` + RedactedBecause *Event `json:"redacted_because,omitempty"` + InviteRoomState []*Event `json:"invite_room_state,omitempty"` + + BeeperHSOrder int64 `json:"com.beeper.hs.order,omitempty"` + BeeperHSSuborder int16 `json:"com.beeper.hs.suborder,omitempty"` + BeeperHSOrderString *BeeperEncodedOrder `json:"com.beeper.hs.order_string,omitempty"` + BeeperFromBackup bool `json:"com.beeper.from_backup,omitempty"` + + ElementSoftFailed bool `json:"io.element.synapse.soft_failed,omitempty"` + ElementPolicyServerSpammy bool `json:"io.element.synapse.policy_server_spammy,omitempty"` +} + +func (us *Unsigned) IsEmpty() bool { + return us.PrevContent == nil && us.PrevSender == "" && us.ReplacesState == "" && us.Age == 0 && us.Membership == "" && + us.TransactionID == "" && us.RedactedBecause == nil && us.InviteRoomState == nil && us.Relations == nil && + us.BeeperHSOrder == 0 && us.BeeperHSSuborder == 0 && us.BeeperHSOrderString.IsZero() && + !us.ElementSoftFailed +} diff --git a/vendor/maunium.net/go/mautrix/event/eventsource.go b/vendor/maunium.net/go/mautrix/event/eventsource.go new file mode 100644 index 00000000..86c1cebe --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/eventsource.go @@ -0,0 +1,72 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "fmt" +) + +// Source represents the part of the sync response that an event came from. +type Source int + +const ( + SourcePresence Source = 1 << iota + SourceJoin + SourceInvite + SourceLeave + SourceAccountData + SourceTimeline + SourceState + SourceEphemeral + SourceToDevice + SourceDecrypted +) + +const primaryTypes = SourcePresence | SourceAccountData | SourceToDevice | SourceTimeline | SourceState +const roomSections = SourceJoin | SourceInvite | SourceLeave +const roomableTypes = SourceAccountData | SourceTimeline | SourceState +const encryptableTypes = roomableTypes | SourceToDevice + +func (es Source) String() string { + var typeName string + switch es & primaryTypes { + case SourcePresence: + typeName = "presence" + case SourceAccountData: + typeName = "account data" + case SourceToDevice: + typeName = "to-device" + case SourceTimeline: + typeName = "timeline" + case SourceState: + typeName = "state" + default: + return fmt.Sprintf("unknown (%d)", es) + } + if es&roomableTypes != 0 { + switch es & roomSections { + case SourceJoin: + typeName = "joined room " + typeName + case SourceInvite: + typeName = "invited room " + typeName + case SourceLeave: + typeName = "left room " + typeName + default: + return fmt.Sprintf("unknown (%s+%d)", typeName, es) + } + es &^= roomSections + } + if es&encryptableTypes != 0 && es&SourceDecrypted != 0 { + typeName += " (decrypted)" + es &^= SourceDecrypted + } + es &^= primaryTypes + if es != 0 { + return fmt.Sprintf("unknown (%s+%d)", typeName, es) + } + return typeName +} diff --git a/vendor/maunium.net/go/mautrix/event/member.go b/vendor/maunium.net/go/mautrix/event/member.go new file mode 100644 index 00000000..9956a36b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/member.go @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "maunium.net/go/mautrix/id" +) + +// Membership is an enum specifying the membership state of a room member. +type Membership string + +func (ms Membership) IsInviteOrJoin() bool { + return ms == MembershipJoin || ms == MembershipInvite +} + +func (ms Membership) IsLeaveOrBan() bool { + return ms == MembershipLeave || ms == MembershipBan +} + +// The allowed membership states as specified in spec section 10.5.5. +const ( + MembershipJoin Membership = "join" + MembershipLeave Membership = "leave" + MembershipInvite Membership = "invite" + MembershipBan Membership = "ban" + MembershipKnock Membership = "knock" +) + +// MemberEventContent represents the content of a m.room.member state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroommember +type MemberEventContent struct { + Membership Membership `json:"membership"` + AvatarURL id.ContentURIString `json:"avatar_url,omitempty"` + Displayname string `json:"displayname,omitempty"` + IsDirect bool `json:"is_direct,omitempty"` + ThirdPartyInvite *ThirdPartyInvite `json:"third_party_invite,omitempty"` + Reason string `json:"reason,omitempty"` + JoinAuthorisedViaUsersServer id.UserID `json:"join_authorised_via_users_server,omitempty"` + MSC3414File *EncryptedFileInfo `json:"org.matrix.msc3414.file,omitempty"` + + MSC4293RedactEvents bool `json:"org.matrix.msc4293.redact_events,omitempty"` +} + +type SignedThirdPartyInvite struct { + Token string `json:"token"` + Signatures map[string]map[id.KeyID]string `json:"signatures,omitempty"` + MXID string `json:"mxid"` +} + +type ThirdPartyInvite struct { + DisplayName string `json:"display_name"` + Signed SignedThirdPartyInvite `json:"signed"` +} + +type ThirdPartyInviteEventContent struct { + DisplayName string `json:"display_name"` + KeyValidityURL string `json:"key_validity_url"` + PublicKey id.Ed25519 `json:"public_key"` + PublicKeys []ThirdPartyInviteKey `json:"public_keys,omitempty"` +} + +type ThirdPartyInviteKey struct { + KeyValidityURL string `json:"key_validity_url,omitempty"` + PublicKey id.Ed25519 `json:"public_key"` +} diff --git a/vendor/maunium.net/go/mautrix/event/message.go b/vendor/maunium.net/go/mautrix/event/message.go new file mode 100644 index 00000000..692382cf --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/message.go @@ -0,0 +1,409 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "html" + "slices" + "strconv" + "strings" + + "maunium.net/go/mautrix/crypto/attachment" + "maunium.net/go/mautrix/id" +) + +// MessageType is the sub-type of a m.room.message event. +// https://spec.matrix.org/v1.2/client-server-api/#mroommessage-msgtypes +type MessageType string + +func (mt MessageType) IsText() bool { + switch mt { + case MsgText, MsgNotice, MsgEmote: + return true + default: + return false + } +} + +func (mt MessageType) IsMedia() bool { + switch mt { + case MsgImage, MsgVideo, MsgAudio, MsgFile, CapMsgSticker: + return true + default: + return false + } +} + +// Msgtypes +const ( + MsgText MessageType = "m.text" + MsgEmote MessageType = "m.emote" + MsgNotice MessageType = "m.notice" + MsgImage MessageType = "m.image" + MsgLocation MessageType = "m.location" + MsgVideo MessageType = "m.video" + MsgAudio MessageType = "m.audio" + MsgFile MessageType = "m.file" + + MsgVerificationRequest MessageType = "m.key.verification.request" + + MsgBeeperGallery MessageType = "com.beeper.gallery" +) + +// Format specifies the format of the formatted_body in m.room.message events. +// https://spec.matrix.org/v1.2/client-server-api/#mroommessage-msgtypes +type Format string + +// Message formats +const ( + FormatHTML Format = "org.matrix.custom.html" +) + +// RedactionEventContent represents the content of a m.room.redaction message event. +// +// https://spec.matrix.org/v1.8/client-server-api/#mroomredaction +type RedactionEventContent struct { + Reason string `json:"reason,omitempty"` + + // The event ID is here as of room v11. In old servers it may only be at the top level. + Redacts id.EventID `json:"redacts,omitempty"` +} + +// ReactionEventContent represents the content of a m.reaction message event. +// This is not yet in a spec release, see https://github.com/matrix-org/matrix-doc/pull/1849 +type ReactionEventContent struct { + RelatesTo RelatesTo `json:"m.relates_to"` +} + +func (content *ReactionEventContent) GetRelatesTo() *RelatesTo { + return &content.RelatesTo +} + +func (content *ReactionEventContent) OptionalGetRelatesTo() *RelatesTo { + return &content.RelatesTo +} + +func (content *ReactionEventContent) SetRelatesTo(rel *RelatesTo) { + content.RelatesTo = *rel +} + +// MessageEventContent represents the content of a m.room.message event. +// +// It is also used to represent m.sticker events, as they are equivalent to m.room.message +// with the exception of the msgtype field. +// +// https://spec.matrix.org/v1.2/client-server-api/#mroommessage +type MessageEventContent struct { + // Base m.room.message fields + MsgType MessageType `json:"msgtype,omitempty"` + Body string `json:"body"` + + // Extra fields for text types + Format Format `json:"format,omitempty"` + FormattedBody string `json:"formatted_body,omitempty"` + + // Extra field for m.location + GeoURI string `json:"geo_uri,omitempty"` + + // Extra fields for media types + URL id.ContentURIString `json:"url,omitempty"` + Info *FileInfo `json:"info,omitempty"` + File *EncryptedFileInfo `json:"file,omitempty"` + + FileName string `json:"filename,omitempty"` + + Mentions *Mentions `json:"m.mentions,omitempty"` + + // Edits and relations + NewContent *MessageEventContent `json:"m.new_content,omitempty"` + RelatesTo *RelatesTo `json:"m.relates_to,omitempty"` + + // In-room verification + To id.UserID `json:"to,omitempty"` + FromDevice id.DeviceID `json:"from_device,omitempty"` + Methods []VerificationMethod `json:"methods,omitempty"` + + replyFallbackRemoved bool + + MessageSendRetry *BeeperRetryMetadata `json:"com.beeper.message_send_retry,omitempty"` + BeeperGalleryImages []*MessageEventContent `json:"com.beeper.gallery.images,omitempty"` + BeeperGalleryCaption string `json:"com.beeper.gallery.caption,omitempty"` + BeeperGalleryCaptionHTML string `json:"com.beeper.gallery.caption_html,omitempty"` + BeeperPerMessageProfile *BeeperPerMessageProfile `json:"com.beeper.per_message_profile,omitempty"` + + BeeperLinkPreviews []*BeeperLinkPreview `json:"com.beeper.linkpreviews,omitempty"` + + BeeperDisappearingTimer *BeeperDisappearingTimer `json:"com.beeper.disappearing_timer,omitempty"` + + MSC1767Audio *MSC1767Audio `json:"org.matrix.msc1767.audio,omitempty"` + MSC3245Voice *MSC3245Voice `json:"org.matrix.msc3245.voice,omitempty"` + + MSC4332BotCommand *BotCommandInput `json:"org.matrix.msc4332.command,omitempty"` +} + +func (content *MessageEventContent) GetCapMsgType() CapabilityMsgType { + switch content.MsgType { + case CapMsgSticker: + return CapMsgSticker + case "": + if content.URL != "" || content.File != nil { + return CapMsgSticker + } + case MsgImage: + return MsgImage + case MsgAudio: + if content.MSC3245Voice != nil { + return CapMsgVoice + } + return MsgAudio + case MsgVideo: + if content.Info != nil && content.Info.MauGIF { + return CapMsgGIF + } + return MsgVideo + case MsgFile: + return MsgFile + } + return "" +} + +func (content *MessageEventContent) GetFileName() string { + if content.FileName != "" { + return content.FileName + } + return content.Body +} + +func (content *MessageEventContent) GetCaption() string { + if content.FileName != "" && content.Body != "" && content.Body != content.FileName { + return content.Body + } + return "" +} + +func (content *MessageEventContent) GetFormattedCaption() string { + if content.Format == FormatHTML && content.FormattedBody != "" { + return content.FormattedBody + } + return "" +} + +func (content *MessageEventContent) GetRelatesTo() *RelatesTo { + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + return content.RelatesTo +} + +func (content *MessageEventContent) OptionalGetRelatesTo() *RelatesTo { + return content.RelatesTo +} + +func (content *MessageEventContent) SetRelatesTo(rel *RelatesTo) { + content.RelatesTo = rel +} + +func (content *MessageEventContent) SetEdit(original id.EventID) { + newContent := *content + content.NewContent = &newContent + content.RelatesTo = (&RelatesTo{}).SetReplace(original) + if content.MsgType == MsgText || content.MsgType == MsgNotice { + content.Body = "* " + content.Body + content.Mentions = &Mentions{} + if content.Format == FormatHTML && len(content.FormattedBody) > 0 { + content.FormattedBody = "* " + content.FormattedBody + } + // If the message is long, remove most of the useless edit fallback to avoid event size issues. + if len(content.Body) > 10000 { + content.FormattedBody = "" + content.Format = "" + content.Body = content.Body[:50] + "[edit fallback cut…]" + } + } +} + +// TextToHTML converts the given text to a HTML-safe representation by escaping HTML characters +// and replacing newlines with
      tags. +func TextToHTML(text string) string { + return strings.ReplaceAll(html.EscapeString(text), "\n", "
      ") +} + +// ReverseTextToHTML reverses the modifications made by TextToHTML, i.e. replaces
      tags with newlines +// and unescapes HTML escape codes. For actually parsing HTML, use the format package instead. +func ReverseTextToHTML(input string) string { + return html.UnescapeString(strings.ReplaceAll(input, "
      ", "\n")) +} + +func (content *MessageEventContent) EnsureHasHTML() { + if len(content.FormattedBody) == 0 || content.Format != FormatHTML { + content.FormattedBody = TextToHTML(content.Body) + content.Format = FormatHTML + } +} + +func (content *MessageEventContent) GetFile() *EncryptedFileInfo { + if content.File == nil { + content.File = &EncryptedFileInfo{} + } + return content.File +} + +func (content *MessageEventContent) GetInfo() *FileInfo { + if content.Info == nil { + content.Info = &FileInfo{} + } + return content.Info +} + +type Mentions struct { + UserIDs []id.UserID `json:"user_ids,omitempty"` + Room bool `json:"room,omitempty"` +} + +func (m *Mentions) Add(userID id.UserID) { + if userID != "" && !slices.Contains(m.UserIDs, userID) { + m.UserIDs = append(m.UserIDs, userID) + } +} + +func (m *Mentions) Has(userID id.UserID) bool { + return m != nil && slices.Contains(m.UserIDs, userID) +} + +func (m *Mentions) Merge(other *Mentions) *Mentions { + if m == nil { + return other + } else if other == nil { + return m + } + return &Mentions{ + UserIDs: slices.Concat(m.UserIDs, other.UserIDs), + Room: m.Room || other.Room, + } +} + +type EncryptedFileInfo struct { + attachment.EncryptedFile + URL id.ContentURIString `json:"url"` +} + +type FileInfo struct { + MimeType string + ThumbnailInfo *FileInfo + ThumbnailURL id.ContentURIString + ThumbnailFile *EncryptedFileInfo + + Blurhash string + AnoaBlurhash string + + MauGIF bool + IsAnimated bool + + Width int + Height int + Duration int + Size int +} + +type serializableFileInfo struct { + MimeType string `json:"mimetype,omitempty"` + ThumbnailInfo *serializableFileInfo `json:"thumbnail_info,omitempty"` + ThumbnailURL id.ContentURIString `json:"thumbnail_url,omitempty"` + ThumbnailFile *EncryptedFileInfo `json:"thumbnail_file,omitempty"` + + Blurhash string `json:"blurhash,omitempty"` + AnoaBlurhash string `json:"xyz.amorgan.blurhash,omitempty"` + + MauGIF bool `json:"fi.mau.gif,omitempty"` + IsAnimated bool `json:"is_animated,omitempty"` + + Width json.Number `json:"w,omitempty"` + Height json.Number `json:"h,omitempty"` + Duration json.Number `json:"duration,omitempty"` + Size json.Number `json:"size,omitempty"` +} + +func (sfi *serializableFileInfo) CopyFrom(fileInfo *FileInfo) *serializableFileInfo { + if fileInfo == nil { + return nil + } + *sfi = serializableFileInfo{ + MimeType: fileInfo.MimeType, + ThumbnailURL: fileInfo.ThumbnailURL, + ThumbnailInfo: (&serializableFileInfo{}).CopyFrom(fileInfo.ThumbnailInfo), + ThumbnailFile: fileInfo.ThumbnailFile, + + MauGIF: fileInfo.MauGIF, + IsAnimated: fileInfo.IsAnimated, + + Blurhash: fileInfo.Blurhash, + AnoaBlurhash: fileInfo.AnoaBlurhash, + } + if fileInfo.Width > 0 { + sfi.Width = json.Number(strconv.Itoa(fileInfo.Width)) + } + if fileInfo.Height > 0 { + sfi.Height = json.Number(strconv.Itoa(fileInfo.Height)) + } + if fileInfo.Size > 0 { + sfi.Size = json.Number(strconv.Itoa(fileInfo.Size)) + + } + if fileInfo.Duration > 0 { + sfi.Duration = json.Number(strconv.Itoa(int(fileInfo.Duration))) + } + return sfi +} + +func (sfi *serializableFileInfo) CopyTo(fileInfo *FileInfo) { + *fileInfo = FileInfo{ + Width: numberToInt(sfi.Width), + Height: numberToInt(sfi.Height), + Size: numberToInt(sfi.Size), + Duration: numberToInt(sfi.Duration), + MimeType: sfi.MimeType, + ThumbnailURL: sfi.ThumbnailURL, + ThumbnailFile: sfi.ThumbnailFile, + MauGIF: sfi.MauGIF, + IsAnimated: sfi.IsAnimated, + Blurhash: sfi.Blurhash, + AnoaBlurhash: sfi.AnoaBlurhash, + } + if sfi.ThumbnailInfo != nil { + fileInfo.ThumbnailInfo = &FileInfo{} + sfi.ThumbnailInfo.CopyTo(fileInfo.ThumbnailInfo) + } +} + +func (fileInfo *FileInfo) UnmarshalJSON(data []byte) error { + sfi := &serializableFileInfo{} + if err := json.Unmarshal(data, sfi); err != nil { + return err + } + sfi.CopyTo(fileInfo) + return nil +} + +func (fileInfo *FileInfo) MarshalJSON() ([]byte, error) { + return json.Marshal((&serializableFileInfo{}).CopyFrom(fileInfo)) +} + +func numberToInt(val json.Number) int { + f64, _ := val.Float64() + if f64 > 0 { + return int(f64) + } + return 0 +} + +func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo { + if fileInfo.ThumbnailInfo == nil { + fileInfo.ThumbnailInfo = &FileInfo{} + } + return fileInfo.ThumbnailInfo +} diff --git a/vendor/maunium.net/go/mautrix/event/poll.go b/vendor/maunium.net/go/mautrix/event/poll.go new file mode 100644 index 00000000..9082f65e --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/poll.go @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +type PollResponseEventContent struct { + RelatesTo RelatesTo `json:"m.relates_to"` + Response struct { + Answers []string `json:"answers"` + } `json:"org.matrix.msc3381.poll.response"` +} + +func (content *PollResponseEventContent) GetRelatesTo() *RelatesTo { + return &content.RelatesTo +} + +func (content *PollResponseEventContent) OptionalGetRelatesTo() *RelatesTo { + if content.RelatesTo.Type == "" { + return nil + } + return &content.RelatesTo +} + +func (content *PollResponseEventContent) SetRelatesTo(rel *RelatesTo) { + content.RelatesTo = *rel +} + +type MSC1767Message struct { + Text string `json:"org.matrix.msc1767.text,omitempty"` + HTML string `json:"org.matrix.msc1767.html,omitempty"` + Message []ExtensibleText `json:"org.matrix.msc1767.message,omitempty"` +} + +type PollStartEventContent struct { + RelatesTo *RelatesTo `json:"m.relates_to,omitempty"` + Mentions *Mentions `json:"m.mentions,omitempty"` + PollStart struct { + Kind string `json:"kind"` + MaxSelections int `json:"max_selections"` + Question MSC1767Message `json:"question"` + Answers []struct { + ID string `json:"id"` + MSC1767Message + } `json:"answers"` + } `json:"org.matrix.msc3381.poll.start"` +} + +func (content *PollStartEventContent) GetRelatesTo() *RelatesTo { + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + return content.RelatesTo +} + +func (content *PollStartEventContent) OptionalGetRelatesTo() *RelatesTo { + return content.RelatesTo +} + +func (content *PollStartEventContent) SetRelatesTo(rel *RelatesTo) { + content.RelatesTo = rel +} diff --git a/vendor/maunium.net/go/mautrix/event/powerlevels.go b/vendor/maunium.net/go/mautrix/event/powerlevels.go new file mode 100644 index 00000000..50df2c1f --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/powerlevels.go @@ -0,0 +1,225 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "math" + "slices" + "sync" + + "go.mau.fi/util/ptr" + "golang.org/x/exp/maps" + + "maunium.net/go/mautrix/id" +) + +// PowerLevelsEventContent represents the content of a m.room.power_levels state event content. +// https://spec.matrix.org/v1.5/client-server-api/#mroompower_levels +type PowerLevelsEventContent struct { + usersLock sync.RWMutex + Users map[id.UserID]int `json:"users,omitempty"` + UsersDefault int `json:"users_default,omitempty"` + + eventsLock sync.RWMutex + Events map[string]int `json:"events,omitempty"` + EventsDefault int `json:"events_default,omitempty"` + + Notifications *NotificationPowerLevels `json:"notifications,omitempty"` + + StateDefaultPtr *int `json:"state_default,omitempty"` + + InvitePtr *int `json:"invite,omitempty"` + KickPtr *int `json:"kick,omitempty"` + BanPtr *int `json:"ban,omitempty"` + RedactPtr *int `json:"redact,omitempty"` + + // This is not a part of power levels, it's added by mautrix-go internally in certain places + // in order to detect creator power accurately. + CreateEvent *Event `json:"-"` +} + +func (pl *PowerLevelsEventContent) Clone() *PowerLevelsEventContent { + if pl == nil { + return nil + } + return &PowerLevelsEventContent{ + Users: maps.Clone(pl.Users), + UsersDefault: pl.UsersDefault, + Events: maps.Clone(pl.Events), + EventsDefault: pl.EventsDefault, + StateDefaultPtr: ptr.Clone(pl.StateDefaultPtr), + + Notifications: pl.Notifications.Clone(), + + InvitePtr: ptr.Clone(pl.InvitePtr), + KickPtr: ptr.Clone(pl.KickPtr), + BanPtr: ptr.Clone(pl.BanPtr), + RedactPtr: ptr.Clone(pl.RedactPtr), + + CreateEvent: pl.CreateEvent, + } +} + +type NotificationPowerLevels struct { + RoomPtr *int `json:"room,omitempty"` +} + +func (npl *NotificationPowerLevels) Clone() *NotificationPowerLevels { + if npl == nil { + return nil + } + return &NotificationPowerLevels{ + RoomPtr: ptr.Clone(npl.RoomPtr), + } +} + +func (npl *NotificationPowerLevels) Room() int { + if npl != nil && npl.RoomPtr != nil { + return *npl.RoomPtr + } + return 50 +} + +func (pl *PowerLevelsEventContent) Invite() int { + if pl.InvitePtr != nil { + return *pl.InvitePtr + } + return 0 +} + +func (pl *PowerLevelsEventContent) Kick() int { + if pl.KickPtr != nil { + return *pl.KickPtr + } + return 50 +} + +func (pl *PowerLevelsEventContent) Ban() int { + if pl.BanPtr != nil { + return *pl.BanPtr + } + return 50 +} + +func (pl *PowerLevelsEventContent) Redact() int { + if pl.RedactPtr != nil { + return *pl.RedactPtr + } + return 50 +} + +func (pl *PowerLevelsEventContent) StateDefault() int { + if pl.StateDefaultPtr != nil { + return *pl.StateDefaultPtr + } + return 50 +} + +func (pl *PowerLevelsEventContent) GetUserLevel(userID id.UserID) int { + if pl.isCreator(userID) { + return math.MaxInt + } + pl.usersLock.RLock() + defer pl.usersLock.RUnlock() + level, ok := pl.Users[userID] + if !ok { + return pl.UsersDefault + } + return level +} + +func (pl *PowerLevelsEventContent) SetUserLevel(userID id.UserID, level int) { + pl.usersLock.Lock() + defer pl.usersLock.Unlock() + if level == pl.UsersDefault { + delete(pl.Users, userID) + } else { + if pl.Users == nil { + pl.Users = make(map[id.UserID]int) + } + pl.Users[userID] = level + } +} + +func (pl *PowerLevelsEventContent) EnsureUserLevel(target id.UserID, level int) bool { + return pl.EnsureUserLevelAs("", target, level) +} + +func (pl *PowerLevelsEventContent) createContent() *CreateEventContent { + if pl.CreateEvent == nil { + return &CreateEventContent{} + } + return pl.CreateEvent.Content.AsCreate() +} + +func (pl *PowerLevelsEventContent) isCreator(userID id.UserID) bool { + cc := pl.createContent() + return cc.SupportsCreatorPower() && (userID == pl.CreateEvent.Sender || slices.Contains(cc.AdditionalCreators, userID)) +} + +func (pl *PowerLevelsEventContent) EnsureUserLevelAs(actor, target id.UserID, level int) bool { + if pl.isCreator(target) { + return false + } + existingLevel := pl.GetUserLevel(target) + if actor != "" && !pl.isCreator(actor) { + actorLevel := pl.GetUserLevel(actor) + if actorLevel <= existingLevel || actorLevel < level { + return false + } + } + if existingLevel != level { + pl.SetUserLevel(target, level) + return true + } + return false +} + +func (pl *PowerLevelsEventContent) GetEventLevel(eventType Type) int { + pl.eventsLock.RLock() + defer pl.eventsLock.RUnlock() + level, ok := pl.Events[eventType.String()] + if !ok { + if eventType.IsState() { + return pl.StateDefault() + } + return pl.EventsDefault + } + return level +} + +func (pl *PowerLevelsEventContent) SetEventLevel(eventType Type, level int) { + pl.eventsLock.Lock() + defer pl.eventsLock.Unlock() + if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) { + delete(pl.Events, eventType.String()) + } else { + if pl.Events == nil { + pl.Events = make(map[string]int) + } + pl.Events[eventType.String()] = level + } +} + +func (pl *PowerLevelsEventContent) EnsureEventLevel(eventType Type, level int) bool { + return pl.EnsureEventLevelAs("", eventType, level) +} + +func (pl *PowerLevelsEventContent) EnsureEventLevelAs(actor id.UserID, eventType Type, level int) bool { + existingLevel := pl.GetEventLevel(eventType) + if actor != "" && !pl.isCreator(actor) { + actorLevel := pl.GetUserLevel(actor) + if existingLevel > actorLevel || level > actorLevel { + return false + } + } + if existingLevel != level { + pl.SetEventLevel(eventType, level) + return true + } + return false +} diff --git a/vendor/maunium.net/go/mautrix/event/relations.go b/vendor/maunium.net/go/mautrix/event/relations.go new file mode 100644 index 00000000..2316cbc7 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/relations.go @@ -0,0 +1,239 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + + "maunium.net/go/mautrix/id" +) + +type RelationType string + +const ( + RelReplace RelationType = "m.replace" + RelReference RelationType = "m.reference" + RelAnnotation RelationType = "m.annotation" + RelThread RelationType = "m.thread" + RelBeeperTranscription RelationType = "com.beeper.transcription" +) + +type RelatesTo struct { + Type RelationType `json:"rel_type,omitempty"` + EventID id.EventID `json:"event_id,omitempty"` + Key string `json:"key,omitempty"` + + InReplyTo *InReplyTo `json:"m.in_reply_to,omitempty"` + IsFallingBack bool `json:"is_falling_back,omitempty"` +} + +type InReplyTo struct { + EventID id.EventID `json:"event_id,omitempty"` + + UnstableRoomID id.RoomID `json:"com.beeper.cross_room_id,omitempty"` +} + +func (rel *RelatesTo) Copy() *RelatesTo { + if rel == nil { + return nil + } + cp := *rel + return &cp +} + +func (rel *RelatesTo) GetReplaceID() id.EventID { + if rel != nil && rel.Type == RelReplace { + return rel.EventID + } + return "" +} + +func (rel *RelatesTo) GetReferenceID() id.EventID { + if rel != nil && rel.Type == RelReference { + return rel.EventID + } + return "" +} + +func (rel *RelatesTo) GetThreadParent() id.EventID { + if rel != nil && rel.Type == RelThread { + return rel.EventID + } + return "" +} + +func (rel *RelatesTo) GetReplyTo() id.EventID { + if rel != nil && rel.InReplyTo != nil { + return rel.InReplyTo.EventID + } + return "" +} + +func (rel *RelatesTo) GetNonFallbackReplyTo() id.EventID { + if rel != nil && rel.InReplyTo != nil && (rel.Type != RelThread || !rel.IsFallingBack) { + return rel.InReplyTo.EventID + } + return "" +} + +func (rel *RelatesTo) GetAnnotationID() id.EventID { + if rel != nil && rel.Type == RelAnnotation { + return rel.EventID + } + return "" +} + +func (rel *RelatesTo) GetAnnotationKey() string { + if rel != nil && rel.Type == RelAnnotation { + return rel.Key + } + return "" +} + +func (rel *RelatesTo) SetReplace(mxid id.EventID) *RelatesTo { + rel.Type = RelReplace + rel.EventID = mxid + return rel +} + +func (rel *RelatesTo) SetReplyTo(mxid id.EventID) *RelatesTo { + if rel.Type != RelThread { + rel.Type = "" + rel.EventID = "" + } + rel.InReplyTo = &InReplyTo{EventID: mxid} + rel.IsFallingBack = false + return rel +} + +func (rel *RelatesTo) SetThread(mxid, fallback id.EventID) *RelatesTo { + rel.Type = RelThread + rel.EventID = mxid + if fallback != "" && rel.GetReplyTo() == "" { + rel.SetReplyTo(fallback) + rel.IsFallingBack = true + } + return rel +} + +func (rel *RelatesTo) SetAnnotation(mxid id.EventID, key string) *RelatesTo { + rel.Type = RelAnnotation + rel.EventID = mxid + rel.Key = key + return rel +} + +type RelationChunkItem struct { + Type RelationType `json:"type"` + EventID string `json:"event_id,omitempty"` + Key string `json:"key,omitempty"` + Count int `json:"count,omitempty"` +} + +type RelationChunk struct { + Chunk []RelationChunkItem `json:"chunk"` + + Limited bool `json:"limited"` + Count int `json:"count"` +} + +type AnnotationChunk struct { + RelationChunk + Map map[string]int `json:"-"` +} + +type serializableAnnotationChunk AnnotationChunk + +func (ac *AnnotationChunk) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, (*serializableAnnotationChunk)(ac)); err != nil { + return err + } + ac.Map = make(map[string]int) + for _, item := range ac.Chunk { + if item.Key != "" { + ac.Map[item.Key] += item.Count + } + } + return nil +} + +func (ac *AnnotationChunk) Serialize() RelationChunk { + ac.Chunk = make([]RelationChunkItem, len(ac.Map)) + i := 0 + for key, count := range ac.Map { + ac.Chunk[i] = RelationChunkItem{ + Type: RelAnnotation, + Key: key, + Count: count, + } + i++ + } + return ac.RelationChunk +} + +type EventIDChunk struct { + RelationChunk + List []string `json:"-"` +} + +type serializableEventIDChunk EventIDChunk + +func (ec *EventIDChunk) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, (*serializableEventIDChunk)(ec)); err != nil { + return err + } + for _, item := range ec.Chunk { + ec.List = append(ec.List, item.EventID) + } + return nil +} + +func (ec *EventIDChunk) Serialize(typ RelationType) RelationChunk { + ec.Chunk = make([]RelationChunkItem, len(ec.List)) + for i, eventID := range ec.List { + ec.Chunk[i] = RelationChunkItem{ + Type: typ, + EventID: eventID, + } + } + return ec.RelationChunk +} + +type Relations struct { + Raw map[RelationType]RelationChunk `json:"-"` + + Annotations AnnotationChunk `json:"m.annotation,omitempty"` + References EventIDChunk `json:"m.reference,omitempty"` + Replaces EventIDChunk `json:"m.replace,omitempty"` +} + +type serializableRelations Relations + +func (relations *Relations) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &relations.Raw); err != nil { + return err + } + return json.Unmarshal(data, (*serializableRelations)(relations)) +} + +func (relations *Relations) MarshalJSON() ([]byte, error) { + if relations.Raw == nil { + relations.Raw = make(map[RelationType]RelationChunk) + } + relations.Raw[RelAnnotation] = relations.Annotations.Serialize() + relations.Raw[RelReference] = relations.References.Serialize(RelReference) + relations.Raw[RelReplace] = relations.Replaces.Serialize(RelReplace) + for key, item := range relations.Raw { + if !item.Limited { + item.Count = len(item.Chunk) + } + if item.Count == 0 { + delete(relations.Raw, key) + } + } + return json.Marshal(relations.Raw) +} diff --git a/vendor/maunium.net/go/mautrix/event/reply.go b/vendor/maunium.net/go/mautrix/event/reply.go new file mode 100644 index 00000000..9ae1c110 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/reply.go @@ -0,0 +1,73 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "regexp" + "strings" + + "maunium.net/go/mautrix/id" +) + +var HTMLReplyFallbackRegex = regexp.MustCompile(`^[\s\S]+?`) + +func TrimReplyFallbackHTML(html string) string { + return HTMLReplyFallbackRegex.ReplaceAllString(html, "") +} + +func TrimReplyFallbackText(text string) string { + if (!strings.HasPrefix(text, "> <") && !strings.HasPrefix(text, "> * <")) || !strings.Contains(text, "\n") { + return text + } + + lines := strings.Split(text, "\n") + for len(lines) > 0 && strings.HasPrefix(lines[0], "> ") { + lines = lines[1:] + } + return strings.TrimSpace(strings.Join(lines, "\n")) +} + +func (content *MessageEventContent) RemoveReplyFallback() { + if len(content.RelatesTo.GetReplyTo()) > 0 && !content.replyFallbackRemoved { + if content.Format == FormatHTML { + content.FormattedBody = TrimReplyFallbackHTML(content.FormattedBody) + } + content.Body = TrimReplyFallbackText(content.Body) + content.replyFallbackRemoved = true + } +} + +// Deprecated: RelatesTo methods are nil-safe, so RelatesTo.GetReplyTo can be used directly +func (content *MessageEventContent) GetReplyTo() id.EventID { + return content.RelatesTo.GetReplyTo() +} + +func (content *MessageEventContent) SetReply(inReplyTo *Event) { + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + content.RelatesTo.SetReplyTo(inReplyTo.ID) + if content.Mentions == nil { + content.Mentions = &Mentions{} + } + content.Mentions.Add(inReplyTo.Sender) +} + +func (content *MessageEventContent) SetThread(inReplyTo *Event) { + root := inReplyTo.ID + relatable, ok := inReplyTo.Content.Parsed.(Relatable) + if ok { + targetRoot := relatable.OptionalGetRelatesTo().GetThreadParent() + if targetRoot != "" { + root = targetRoot + } + } + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + content.RelatesTo.SetThread(root, inReplyTo.ID) +} diff --git a/vendor/maunium.net/go/mautrix/event/state.go b/vendor/maunium.net/go/mautrix/event/state.go new file mode 100644 index 00000000..ed5434c9 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/state.go @@ -0,0 +1,322 @@ +// Copyright (c) 2021 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/base64" + "encoding/json" + "slices" + + "go.mau.fi/util/jsontime" + + "maunium.net/go/mautrix/id" +) + +// CanonicalAliasEventContent represents the content of a m.room.canonical_alias state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomcanonical_alias +type CanonicalAliasEventContent struct { + Alias id.RoomAlias `json:"alias"` + AltAliases []id.RoomAlias `json:"alt_aliases,omitempty"` +} + +// RoomNameEventContent represents the content of a m.room.name state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomname +type RoomNameEventContent struct { + Name string `json:"name"` +} + +// RoomAvatarEventContent represents the content of a m.room.avatar state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomavatar +type RoomAvatarEventContent struct { + URL id.ContentURIString `json:"url,omitempty"` + Info *FileInfo `json:"info,omitempty"` + MSC3414File *EncryptedFileInfo `json:"org.matrix.msc3414.file,omitempty"` +} + +// ServerACLEventContent represents the content of a m.room.server_acl state event. +// https://spec.matrix.org/v1.2/client-server-api/#server-access-control-lists-acls-for-rooms +type ServerACLEventContent struct { + Allow []string `json:"allow,omitempty"` + AllowIPLiterals bool `json:"allow_ip_literals"` + Deny []string `json:"deny,omitempty"` +} + +// TopicEventContent represents the content of a m.room.topic state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomtopic +type TopicEventContent struct { + Topic string `json:"topic"` + ExtensibleTopic *ExtensibleTopic `json:"m.topic,omitempty"` +} + +// ExtensibleTopic represents the contents of the m.topic field within the +// m.room.topic state event as described in [MSC3765]. +// +// [MSC3765]: https://github.com/matrix-org/matrix-spec-proposals/pull/3765 +type ExtensibleTopic = ExtensibleTextContainer + +type ExtensibleTextContainer struct { + Text []ExtensibleText `json:"m.text"` +} + +func MakeExtensibleText(text string) *ExtensibleTextContainer { + return &ExtensibleTextContainer{ + Text: []ExtensibleText{{ + Body: text, + MimeType: "text/plain", + }}, + } +} + +func MakeExtensibleFormattedText(plaintext, html string) *ExtensibleTextContainer { + return &ExtensibleTextContainer{ + Text: []ExtensibleText{{ + Body: plaintext, + MimeType: "text/plain", + }, { + Body: html, + MimeType: "text/html", + }}, + } +} + +// ExtensibleText represents the contents of an m.text field. +type ExtensibleText struct { + MimeType string `json:"mimetype,omitempty"` + Body string `json:"body"` +} + +// TombstoneEventContent represents the content of a m.room.tombstone state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomtombstone +type TombstoneEventContent struct { + Body string `json:"body"` + ReplacementRoom id.RoomID `json:"replacement_room"` +} + +type Predecessor struct { + RoomID id.RoomID `json:"room_id"` + EventID id.EventID `json:"event_id"` +} + +// Deprecated: use id.RoomVersion instead +type RoomVersion = id.RoomVersion + +// Deprecated: use id.RoomVX constants instead +const ( + RoomV1 = id.RoomV1 + RoomV2 = id.RoomV2 + RoomV3 = id.RoomV3 + RoomV4 = id.RoomV4 + RoomV5 = id.RoomV5 + RoomV6 = id.RoomV6 + RoomV7 = id.RoomV7 + RoomV8 = id.RoomV8 + RoomV9 = id.RoomV9 + RoomV10 = id.RoomV10 + RoomV11 = id.RoomV11 + RoomV12 = id.RoomV12 +) + +// CreateEventContent represents the content of a m.room.create state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomcreate +type CreateEventContent struct { + Type RoomType `json:"type,omitempty"` + Federate *bool `json:"m.federate,omitempty"` + RoomVersion id.RoomVersion `json:"room_version,omitempty"` + Predecessor *Predecessor `json:"predecessor,omitempty"` + + // Room v12+ only + AdditionalCreators []id.UserID `json:"additional_creators,omitempty"` + + // Deprecated: use the event sender instead + Creator id.UserID `json:"creator,omitempty"` +} + +func (cec *CreateEventContent) SupportsCreatorPower() bool { + if cec == nil { + return false + } + return cec.RoomVersion.PrivilegedRoomCreators() +} + +// JoinRule specifies how open a room is to new members. +// https://spec.matrix.org/v1.2/client-server-api/#mroomjoin_rules +type JoinRule string + +const ( + JoinRulePublic JoinRule = "public" + JoinRuleKnock JoinRule = "knock" + JoinRuleInvite JoinRule = "invite" + JoinRuleRestricted JoinRule = "restricted" + JoinRuleKnockRestricted JoinRule = "knock_restricted" + JoinRulePrivate JoinRule = "private" +) + +// JoinRulesEventContent represents the content of a m.room.join_rules state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomjoin_rules +type JoinRulesEventContent struct { + JoinRule JoinRule `json:"join_rule"` + Allow []JoinRuleAllow `json:"allow,omitempty"` +} + +type JoinRuleAllowType string + +const ( + JoinRuleAllowRoomMembership JoinRuleAllowType = "m.room_membership" +) + +type JoinRuleAllow struct { + RoomID id.RoomID `json:"room_id"` + Type JoinRuleAllowType `json:"type"` +} + +// PinnedEventsEventContent represents the content of a m.room.pinned_events state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroompinned_events +type PinnedEventsEventContent struct { + Pinned []id.EventID `json:"pinned"` +} + +// HistoryVisibility specifies who can see new messages. +// https://spec.matrix.org/v1.2/client-server-api/#mroomhistory_visibility +type HistoryVisibility string + +const ( + HistoryVisibilityInvited HistoryVisibility = "invited" + HistoryVisibilityJoined HistoryVisibility = "joined" + HistoryVisibilityShared HistoryVisibility = "shared" + HistoryVisibilityWorldReadable HistoryVisibility = "world_readable" +) + +// HistoryVisibilityEventContent represents the content of a m.room.history_visibility state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomhistory_visibility +type HistoryVisibilityEventContent struct { + HistoryVisibility HistoryVisibility `json:"history_visibility"` +} + +// GuestAccess specifies whether or not guest accounts can join. +// https://spec.matrix.org/v1.2/client-server-api/#mroomguest_access +type GuestAccess string + +const ( + GuestAccessCanJoin GuestAccess = "can_join" + GuestAccessForbidden GuestAccess = "forbidden" +) + +// GuestAccessEventContent represents the content of a m.room.guest_access state event. +// https://spec.matrix.org/v1.2/client-server-api/#mroomguest_access +type GuestAccessEventContent struct { + GuestAccess GuestAccess `json:"guest_access"` +} + +type BridgeInfoSection struct { + ID string `json:"id"` + DisplayName string `json:"displayname,omitempty"` + AvatarURL id.ContentURIString `json:"avatar_url,omitempty"` + ExternalURL string `json:"external_url,omitempty"` + + Receiver string `json:"fi.mau.receiver,omitempty"` +} + +// BridgeEventContent represents the content of a m.bridge state event. +// https://github.com/matrix-org/matrix-doc/pull/2346 +type BridgeEventContent struct { + BridgeBot id.UserID `json:"bridgebot"` + Creator id.UserID `json:"creator,omitempty"` + Protocol BridgeInfoSection `json:"protocol"` + Network *BridgeInfoSection `json:"network,omitempty"` + Channel BridgeInfoSection `json:"channel"` + + BeeperRoomType string `json:"com.beeper.room_type,omitempty"` + BeeperRoomTypeV2 string `json:"com.beeper.room_type.v2,omitempty"` + + TempSlackRemoteIDMigratedFlag bool `json:"com.beeper.slack_remote_id_migrated,omitempty"` +} + +// DisappearingType represents the type of a disappearing message timer. +type DisappearingType string + +const ( + DisappearingTypeNone DisappearingType = "" + DisappearingTypeAfterRead DisappearingType = "after_read" + DisappearingTypeAfterSend DisappearingType = "after_send" +) + +type BeeperDisappearingTimer struct { + Type DisappearingType `json:"type"` + Timer jsontime.Milliseconds `json:"timer"` +} + +type marshalableBeeperDisappearingTimer BeeperDisappearingTimer + +func (bdt *BeeperDisappearingTimer) MarshalJSON() ([]byte, error) { + if bdt == nil || bdt.Type == DisappearingTypeNone { + return []byte("{}"), nil + } + return json.Marshal((*marshalableBeeperDisappearingTimer)(bdt)) +} + +type SpaceChildEventContent struct { + Via []string `json:"via,omitempty"` + Order string `json:"order,omitempty"` + Suggested bool `json:"suggested,omitempty"` +} + +type SpaceParentEventContent struct { + Via []string `json:"via,omitempty"` + Canonical bool `json:"canonical,omitempty"` +} + +type PolicyRecommendation string + +const ( + PolicyRecommendationBan PolicyRecommendation = "m.ban" + PolicyRecommendationUnstableTakedown PolicyRecommendation = "org.matrix.msc4204.takedown" + PolicyRecommendationUnstableBan PolicyRecommendation = "org.matrix.mjolnir.ban" + PolicyRecommendationUnban PolicyRecommendation = "fi.mau.meowlnir.unban" +) + +type PolicyHashes struct { + SHA256 string `json:"sha256"` +} + +func (ph *PolicyHashes) DecodeSHA256() *[32]byte { + if ph == nil || ph.SHA256 == "" { + return nil + } + decoded, _ := base64.StdEncoding.DecodeString(ph.SHA256) + if len(decoded) == 32 { + return (*[32]byte)(decoded) + } + return nil +} + +// ModPolicyContent represents the content of a m.room.rule.user, m.room.rule.room, and m.room.rule.server state event. +// https://spec.matrix.org/v1.2/client-server-api/#moderation-policy-lists +type ModPolicyContent struct { + Entity string `json:"entity,omitempty"` + Reason string `json:"reason"` + Recommendation PolicyRecommendation `json:"recommendation"` + UnstableHashes *PolicyHashes `json:"org.matrix.msc4205.hashes,omitempty"` +} + +func (mpc *ModPolicyContent) EntityOrHash() string { + if mpc.UnstableHashes != nil && mpc.UnstableHashes.SHA256 != "" { + return mpc.UnstableHashes.SHA256 + } + return mpc.Entity +} + +type ElementFunctionalMembersContent struct { + ServiceMembers []id.UserID `json:"service_members"` +} + +func (efmc *ElementFunctionalMembersContent) Add(mxid id.UserID) bool { + if slices.Contains(efmc.ServiceMembers, mxid) { + return false + } + efmc.ServiceMembers = append(efmc.ServiceMembers, mxid) + return true +} diff --git a/vendor/maunium.net/go/mautrix/event/type.go b/vendor/maunium.net/go/mautrix/event/type.go new file mode 100644 index 00000000..56ea82f6 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/type.go @@ -0,0 +1,296 @@ +// Copyright (c) 2021 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "fmt" + "strings" + + "maunium.net/go/mautrix/id" +) + +type RoomType string + +const ( + RoomTypeDefault RoomType = "" + RoomTypeSpace RoomType = "m.space" +) + +type TypeClass int + +func (tc TypeClass) Name() string { + switch tc { + case MessageEventType: + return "message" + case StateEventType: + return "state" + case EphemeralEventType: + return "ephemeral" + case AccountDataEventType: + return "account data" + case ToDeviceEventType: + return "to-device" + default: + return "unknown" + } +} + +const ( + // Unknown events + UnknownEventType TypeClass = iota + // Normal message events + MessageEventType + // State events + StateEventType + // Ephemeral events + EphemeralEventType + // Account data events + AccountDataEventType + // Device-to-device events + ToDeviceEventType +) + +type Type struct { + Type string + Class TypeClass +} + +func NewEventType(name string) Type { + evtType := Type{Type: name} + evtType.Class = evtType.GuessClass() + return evtType +} + +func (et *Type) IsState() bool { + return et.Class == StateEventType +} + +func (et *Type) IsEphemeral() bool { + return et.Class == EphemeralEventType +} + +func (et *Type) IsAccountData() bool { + return et.Class == AccountDataEventType +} + +func (et *Type) IsToDevice() bool { + return et.Class == ToDeviceEventType +} + +func (et *Type) IsInRoomVerification() bool { + switch et.Type { + case InRoomVerificationStart.Type, InRoomVerificationReady.Type, InRoomVerificationAccept.Type, + InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type: + return true + default: + return false + } +} + +func (et *Type) IsCall() bool { + switch et.Type { + case CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type, + CallNegotiate.Type, CallHangup.Type: + return true + default: + return false + } +} + +func (et *Type) IsCustom() bool { + return !strings.HasPrefix(et.Type, "m.") +} + +func (et *Type) GuessClass() TypeClass { + switch et.Type { + case StateAliases.Type, StateCanonicalAlias.Type, StateCreate.Type, StateJoinRules.Type, StateMember.Type, StateThirdPartyInvite.Type, + StatePowerLevels.Type, StateRoomName.Type, StateRoomAvatar.Type, StateServerACL.Type, StateTopic.Type, + StatePinnedEvents.Type, StateTombstone.Type, StateEncryption.Type, StateBridge.Type, StateHalfShotBridge.Type, + StateSpaceParent.Type, StateSpaceChild.Type, StatePolicyRoom.Type, StatePolicyServer.Type, StatePolicyUser.Type, + StateElementFunctionalMembers.Type, StateBeeperRoomFeatures.Type, StateBeeperDisappearingTimer.Type, + StateBotCommands.Type: + return StateEventType + case EphemeralEventReceipt.Type, EphemeralEventTyping.Type, EphemeralEventPresence.Type: + return EphemeralEventType + case AccountDataDirectChats.Type, AccountDataPushRules.Type, AccountDataRoomTags.Type, + AccountDataFullyRead.Type, AccountDataIgnoredUserList.Type, AccountDataMarkedUnread.Type, + AccountDataSecretStorageKey.Type, AccountDataSecretStorageDefaultKey.Type, + AccountDataCrossSigningMaster.Type, AccountDataCrossSigningSelf.Type, AccountDataCrossSigningUser.Type, + AccountDataFullyRead.Type, AccountDataMegolmBackupKey.Type: + return AccountDataEventType + case EventRedaction.Type, EventMessage.Type, EventEncrypted.Type, EventReaction.Type, EventSticker.Type, + InRoomVerificationStart.Type, InRoomVerificationReady.Type, InRoomVerificationAccept.Type, + InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type, + CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type, + CallNegotiate.Type, CallHangup.Type, BeeperMessageStatus.Type, EventUnstablePollStart.Type, EventUnstablePollResponse.Type, + EventUnstablePollEnd.Type, BeeperTranscription.Type, BeeperDeleteChat.Type: + return MessageEventType + case ToDeviceRoomKey.Type, ToDeviceRoomKeyRequest.Type, ToDeviceForwardedRoomKey.Type, ToDeviceRoomKeyWithheld.Type, + ToDeviceBeeperRoomKeyAck.Type: + return ToDeviceEventType + default: + return UnknownEventType + } +} + +func (et *Type) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &et.Type) + if err != nil { + return err + } + et.Class = et.GuessClass() + return nil +} + +func (et *Type) MarshalJSON() ([]byte, error) { + return json.Marshal(&et.Type) +} + +func (et *Type) UnmarshalText(data []byte) error { + et.Type = string(data) + et.Class = et.GuessClass() + return nil +} + +func (et Type) MarshalText() ([]byte, error) { + return []byte(et.Type), nil +} + +func (et Type) String() string { + return et.Type +} + +func (et Type) Repr() string { + return fmt.Sprintf("%s (%s)", et.Type, et.Class.Name()) +} + +// State events +var ( + StateAliases = Type{"m.room.aliases", StateEventType} + StateCanonicalAlias = Type{"m.room.canonical_alias", StateEventType} + StateCreate = Type{"m.room.create", StateEventType} + StateJoinRules = Type{"m.room.join_rules", StateEventType} + StateHistoryVisibility = Type{"m.room.history_visibility", StateEventType} + StateGuestAccess = Type{"m.room.guest_access", StateEventType} + StateMember = Type{"m.room.member", StateEventType} + StateThirdPartyInvite = Type{"m.room.third_party_invite", StateEventType} + StatePowerLevels = Type{"m.room.power_levels", StateEventType} + StateRoomName = Type{"m.room.name", StateEventType} + StateTopic = Type{"m.room.topic", StateEventType} + StateRoomAvatar = Type{"m.room.avatar", StateEventType} + StatePinnedEvents = Type{"m.room.pinned_events", StateEventType} + StateServerACL = Type{"m.room.server_acl", StateEventType} + StateTombstone = Type{"m.room.tombstone", StateEventType} + StatePolicyRoom = Type{"m.policy.rule.room", StateEventType} + StatePolicyServer = Type{"m.policy.rule.server", StateEventType} + StatePolicyUser = Type{"m.policy.rule.user", StateEventType} + StateEncryption = Type{"m.room.encryption", StateEventType} + StateBridge = Type{"m.bridge", StateEventType} + StateHalfShotBridge = Type{"uk.half-shot.bridge", StateEventType} + StateSpaceChild = Type{"m.space.child", StateEventType} + StateSpaceParent = Type{"m.space.parent", StateEventType} + + StateLegacyPolicyRoom = Type{"m.room.rule.room", StateEventType} + StateLegacyPolicyServer = Type{"m.room.rule.server", StateEventType} + StateLegacyPolicyUser = Type{"m.room.rule.user", StateEventType} + StateUnstablePolicyRoom = Type{"org.matrix.mjolnir.rule.room", StateEventType} + StateUnstablePolicyServer = Type{"org.matrix.mjolnir.rule.server", StateEventType} + StateUnstablePolicyUser = Type{"org.matrix.mjolnir.rule.user", StateEventType} + + StateElementFunctionalMembers = Type{"io.element.functional_members", StateEventType} + StateBeeperRoomFeatures = Type{"com.beeper.room_features", StateEventType} + StateBeeperDisappearingTimer = Type{"com.beeper.disappearing_timer", StateEventType} + StateBotCommands = Type{"org.matrix.msc4332.commands", StateEventType} +) + +// Message events +var ( + EventRedaction = Type{"m.room.redaction", MessageEventType} + EventMessage = Type{"m.room.message", MessageEventType} + EventEncrypted = Type{"m.room.encrypted", MessageEventType} + EventReaction = Type{"m.reaction", MessageEventType} + EventSticker = Type{"m.sticker", MessageEventType} + + InRoomVerificationReady = Type{"m.key.verification.ready", MessageEventType} + InRoomVerificationStart = Type{"m.key.verification.start", MessageEventType} + InRoomVerificationDone = Type{"m.key.verification.done", MessageEventType} + InRoomVerificationCancel = Type{"m.key.verification.cancel", MessageEventType} + + // SAS Verification Events + InRoomVerificationAccept = Type{"m.key.verification.accept", MessageEventType} + InRoomVerificationKey = Type{"m.key.verification.key", MessageEventType} + InRoomVerificationMAC = Type{"m.key.verification.mac", MessageEventType} + + CallInvite = Type{"m.call.invite", MessageEventType} + CallCandidates = Type{"m.call.candidates", MessageEventType} + CallAnswer = Type{"m.call.answer", MessageEventType} + CallReject = Type{"m.call.reject", MessageEventType} + CallSelectAnswer = Type{"m.call.select_answer", MessageEventType} + CallNegotiate = Type{"m.call.negotiate", MessageEventType} + CallHangup = Type{"m.call.hangup", MessageEventType} + + BeeperMessageStatus = Type{"com.beeper.message_send_status", MessageEventType} + BeeperTranscription = Type{"com.beeper.transcription", MessageEventType} + BeeperDeleteChat = Type{"com.beeper.delete_chat", MessageEventType} + + EventUnstablePollStart = Type{Type: "org.matrix.msc3381.poll.start", Class: MessageEventType} + EventUnstablePollResponse = Type{Type: "org.matrix.msc3381.poll.response", Class: MessageEventType} + EventUnstablePollEnd = Type{Type: "org.matrix.msc3381.poll.end", Class: MessageEventType} +) + +// Ephemeral events +var ( + EphemeralEventReceipt = Type{"m.receipt", EphemeralEventType} + EphemeralEventTyping = Type{"m.typing", EphemeralEventType} + EphemeralEventPresence = Type{"m.presence", EphemeralEventType} +) + +// Account data events +var ( + AccountDataDirectChats = Type{"m.direct", AccountDataEventType} + AccountDataPushRules = Type{"m.push_rules", AccountDataEventType} + AccountDataRoomTags = Type{"m.tag", AccountDataEventType} + AccountDataFullyRead = Type{"m.fully_read", AccountDataEventType} + AccountDataIgnoredUserList = Type{"m.ignored_user_list", AccountDataEventType} + AccountDataMarkedUnread = Type{"m.marked_unread", AccountDataEventType} + AccountDataBeeperMute = Type{"com.beeper.mute", AccountDataEventType} + + AccountDataSecretStorageDefaultKey = Type{"m.secret_storage.default_key", AccountDataEventType} + AccountDataSecretStorageKey = Type{"m.secret_storage.key", AccountDataEventType} + AccountDataCrossSigningMaster = Type{string(id.SecretXSMaster), AccountDataEventType} + AccountDataCrossSigningUser = Type{string(id.SecretXSUserSigning), AccountDataEventType} + AccountDataCrossSigningSelf = Type{string(id.SecretXSSelfSigning), AccountDataEventType} + AccountDataMegolmBackupKey = Type{string(id.SecretMegolmBackupV1), AccountDataEventType} +) + +// Device-to-device events +var ( + ToDeviceRoomKey = Type{"m.room_key", ToDeviceEventType} + ToDeviceRoomKeyRequest = Type{"m.room_key_request", ToDeviceEventType} + ToDeviceForwardedRoomKey = Type{"m.forwarded_room_key", ToDeviceEventType} + ToDeviceEncrypted = Type{"m.room.encrypted", ToDeviceEventType} + ToDeviceRoomKeyWithheld = Type{"m.room_key.withheld", ToDeviceEventType} + ToDeviceSecretRequest = Type{"m.secret.request", ToDeviceEventType} + ToDeviceSecretSend = Type{"m.secret.send", ToDeviceEventType} + ToDeviceDummy = Type{"m.dummy", ToDeviceEventType} + + ToDeviceVerificationRequest = Type{"m.key.verification.request", ToDeviceEventType} + ToDeviceVerificationReady = Type{"m.key.verification.ready", ToDeviceEventType} + ToDeviceVerificationStart = Type{"m.key.verification.start", ToDeviceEventType} + ToDeviceVerificationDone = Type{"m.key.verification.done", ToDeviceEventType} + ToDeviceVerificationCancel = Type{"m.key.verification.cancel", ToDeviceEventType} + + // SAS Verification Events + ToDeviceVerificationAccept = Type{"m.key.verification.accept", ToDeviceEventType} + ToDeviceVerificationKey = Type{"m.key.verification.key", ToDeviceEventType} + ToDeviceVerificationMAC = Type{"m.key.verification.mac", ToDeviceEventType} + + ToDeviceOrgMatrixRoomKeyWithheld = Type{"org.matrix.room_key.withheld", ToDeviceEventType} + + ToDeviceBeeperRoomKeyAck = Type{"com.beeper.room_key.ack", ToDeviceEventType} +) diff --git a/vendor/maunium.net/go/mautrix/event/verification.go b/vendor/maunium.net/go/mautrix/event/verification.go new file mode 100644 index 00000000..6101896f --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/verification.go @@ -0,0 +1,308 @@ +// Copyright (c) 2020 Nikos Filippakis +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "go.mau.fi/util/jsonbytes" + "go.mau.fi/util/jsontime" + + "maunium.net/go/mautrix/id" +) + +type VerificationMethod string + +const ( + VerificationMethodSAS VerificationMethod = "m.sas.v1" + + VerificationMethodReciprocate VerificationMethod = "m.reciprocate.v1" + VerificationMethodQRCodeShow VerificationMethod = "m.qr_code.show.v1" + VerificationMethodQRCodeScan VerificationMethod = "m.qr_code.scan.v1" +) + +type VerificationTransactionable interface { + GetTransactionID() id.VerificationTransactionID + SetTransactionID(id.VerificationTransactionID) +} + +// ToDeviceVerificationEvent contains the fields common to all to-device +// verification events. +type ToDeviceVerificationEvent struct { + // TransactionID is an opaque identifier for the verification request. Must + // be unique with respect to the devices involved. + TransactionID id.VerificationTransactionID `json:"transaction_id,omitempty"` +} + +var _ VerificationTransactionable = (*ToDeviceVerificationEvent)(nil) + +func (ve *ToDeviceVerificationEvent) GetTransactionID() id.VerificationTransactionID { + return ve.TransactionID +} + +func (ve *ToDeviceVerificationEvent) SetTransactionID(id id.VerificationTransactionID) { + ve.TransactionID = id +} + +// InRoomVerificationEvent contains the fields common to all in-room +// verification events. +type InRoomVerificationEvent struct { + // RelatesTo indicates the m.key.verification.request that this message is + // related to. Note that for encrypted messages, this property should be in + // the unencrypted portion of the event. + RelatesTo *RelatesTo `json:"m.relates_to,omitempty"` +} + +var _ Relatable = (*InRoomVerificationEvent)(nil) + +func (ve *InRoomVerificationEvent) GetRelatesTo() *RelatesTo { + if ve.RelatesTo == nil { + ve.RelatesTo = &RelatesTo{} + } + return ve.RelatesTo +} + +func (ve *InRoomVerificationEvent) OptionalGetRelatesTo() *RelatesTo { + return ve.RelatesTo +} + +func (ve *InRoomVerificationEvent) SetRelatesTo(rel *RelatesTo) { + ve.RelatesTo = rel +} + +// VerificationRequestEventContent represents the content of an +// [m.key.verification.request] to-device event as described in [Section +// 11.12.2.1] of the Spec. +// +// For the in-room version, use a standard [MessageEventContent] struct. +// +// [m.key.verification.request]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationrequest +// [Section 11.12.2.1]: https://spec.matrix.org/v1.9/client-server-api/#key-verification-framework +type VerificationRequestEventContent struct { + ToDeviceVerificationEvent + // FromDevice is the device ID which is initiating the request. + FromDevice id.DeviceID `json:"from_device"` + // Methods is a list of the verification methods supported by the sender. + Methods []VerificationMethod `json:"methods"` + // Timestamp is the time at which the request was made. + Timestamp jsontime.UnixMilli `json:"timestamp,omitempty"` +} + +// VerificationRequestEventContentFromMessage converts an in-room verification +// request message event to a [VerificationRequestEventContent]. +func VerificationRequestEventContentFromMessage(evt *Event) *VerificationRequestEventContent { + content := evt.Content.AsMessage() + return &VerificationRequestEventContent{ + ToDeviceVerificationEvent: ToDeviceVerificationEvent{ + TransactionID: id.VerificationTransactionID(evt.ID), + }, + Timestamp: jsontime.UMInt(evt.Timestamp), + FromDevice: content.FromDevice, + Methods: content.Methods, + } +} + +// VerificationReadyEventContent represents the content of an +// [m.key.verification.ready] event (both the to-device and the in-room +// version) as described in [Section 11.12.2.1] of the Spec. +// +// [m.key.verification.ready]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationready +// [Section 11.12.2.1]: https://spec.matrix.org/v1.9/client-server-api/#key-verification-framework +type VerificationReadyEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent + + // FromDevice is the device ID which is initiating the request. + FromDevice id.DeviceID `json:"from_device"` + // Methods is a list of the verification methods supported by the sender. + Methods []VerificationMethod `json:"methods"` +} + +type KeyAgreementProtocol string + +const ( + KeyAgreementProtocolCurve25519 KeyAgreementProtocol = "curve25519" + KeyAgreementProtocolCurve25519HKDFSHA256 KeyAgreementProtocol = "curve25519-hkdf-sha256" +) + +type VerificationHashMethod string + +const VerificationHashMethodSHA256 VerificationHashMethod = "sha256" + +type MACMethod string + +const ( + MACMethodHKDFHMACSHA256 MACMethod = "hkdf-hmac-sha256" + MACMethodHKDFHMACSHA256V2 MACMethod = "hkdf-hmac-sha256.v2" +) + +type SASMethod string + +const ( + SASMethodDecimal SASMethod = "decimal" + SASMethodEmoji SASMethod = "emoji" +) + +// VerificationStartEventContent represents the content of an +// [m.key.verification.start] event (both the to-device and the in-room +// version) as described in [Section 11.12.2.1] of the Spec. +// +// This struct also contains the fields for an [m.key.verification.start] event +// using the [VerificationMethodSAS] method as described in [Section +// 11.12.2.2.2] and an [m.key.verification.start] using +// [VerificationMethodReciprocate] as described in [Section 11.12.2.4.2]. +// +// [m.key.verification.start]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationstart +// [Section 11.12.2.1]: https://spec.matrix.org/v1.9/client-server-api/#key-verification-framework +// [Section 11.12.2.2.2]: https://spec.matrix.org/v1.9/client-server-api/#verification-messages-specific-to-sas +// [Section 11.12.2.4.2]: https://spec.matrix.org/v1.9/client-server-api/#verification-messages-specific-to-qr-codes +type VerificationStartEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent + + // FromDevice is the device ID which is initiating the request. + FromDevice id.DeviceID `json:"from_device"` + // Method is the verification method to use. + Method VerificationMethod `json:"method"` + // NextMethod is an optional method to use to verify the other user's key. + // Applicable when the method chosen only verifies one user’s key. This + // field will never be present if the method verifies keys both ways. + NextMethod VerificationMethod `json:"next_method,omitempty"` + + // Hashes are the hash methods the sending device understands. This field + // is only applicable when the method is m.sas.v1. + Hashes []VerificationHashMethod `json:"hashes,omitempty"` + // KeyAgreementProtocols is the list of key agreement protocols the sending + // device understands. This field is only applicable when the method is + // m.sas.v1. + KeyAgreementProtocols []KeyAgreementProtocol `json:"key_agreement_protocols,omitempty"` + // MessageAuthenticationCodes is a list of the MAC methods that the sending + // device understands. This field is only applicable when the method is + // m.sas.v1. + MessageAuthenticationCodes []MACMethod `json:"message_authentication_codes"` + // ShortAuthenticationString is a list of SAS methods the sending device + // (and the sending device's user) understands. This field is only + // applicable when the method is m.sas.v1. + ShortAuthenticationString []SASMethod `json:"short_authentication_string"` + + // Secret is the shared secret from the QR code. This field is only + // applicable when the method is m.reciprocate.v1. + Secret jsonbytes.UnpaddedBytes `json:"secret,omitempty"` +} + +// VerificationDoneEventContent represents the content of an +// [m.key.verification.done] event (both the to-device and the in-room version) +// as described in [Section 11.12.2.1] of the Spec. +// +// This type is an alias for [VerificationRelatable] since there are no +// additional fields defined by the spec. +// +// [m.key.verification.done]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationdone +// [Section 11.12.2.1]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationdone +type VerificationDoneEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent +} + +type VerificationCancelCode string + +const ( + VerificationCancelCodeUser VerificationCancelCode = "m.user" + VerificationCancelCodeTimeout VerificationCancelCode = "m.timeout" + VerificationCancelCodeUnknownTransaction VerificationCancelCode = "m.unknown_transaction" + VerificationCancelCodeUnknownMethod VerificationCancelCode = "m.unknown_method" + VerificationCancelCodeUnexpectedMessage VerificationCancelCode = "m.unexpected_message" + VerificationCancelCodeKeyMismatch VerificationCancelCode = "m.key_mismatch" + VerificationCancelCodeUserMismatch VerificationCancelCode = "m.user_mismatch" + VerificationCancelCodeInvalidMessage VerificationCancelCode = "m.invalid_message" + VerificationCancelCodeAccepted VerificationCancelCode = "m.accepted" + VerificationCancelCodeSASMismatch VerificationCancelCode = "m.mismatched_sas" + VerificationCancelCodeCommitmentMismatch VerificationCancelCode = "m.mismatched_commitment" + + // Non-spec codes + VerificationCancelCodeInternalError VerificationCancelCode = "com.beeper.internal_error" + VerificationCancelCodeMasterKeyNotTrusted VerificationCancelCode = "com.beeper.master_key_not_trusted" // the master key is not trusted by this device, but the QR code that was scanned was from a device that doesn't trust the master key +) + +// VerificationCancelEventContent represents the content of an +// [m.key.verification.cancel] event (both the to-device and the in-room +// version) as described in [Section 11.12.2.1] of the Spec. +// +// [m.key.verification.cancel]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationcancel +// [Section 11.12.2.1]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationdone +type VerificationCancelEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent + + // Code is the error code for why the process/request was cancelled by the + // user. + Code VerificationCancelCode `json:"code"` + // Reason is a human readable description of the code. The client should + // only rely on this string if it does not understand the code. + Reason string `json:"reason"` +} + +// VerificationAcceptEventContent represents the content of an +// [m.key.verification.accept] event (both the to-device and the in-room +// version) as described in [Section 11.12.2.2.2] of the Spec. +// +// [m.key.verification.accept]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationaccept +// [Section 11.12.2.2.2]: https://spec.matrix.org/v1.9/client-server-api/#verification-messages-specific-to-sas +type VerificationAcceptEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent + + // Commitment is the hash of the concatenation of the device's ephemeral + // public key (encoded as unpadded base64) and the canonical JSON + // representation of the m.key.verification.start message. + Commitment jsonbytes.UnpaddedBytes `json:"commitment"` + // Hash is the hash method the device is choosing to use, out of the + // options in the m.key.verification.start message. + Hash VerificationHashMethod `json:"hash"` + // KeyAgreementProtocol is the key agreement protocol the device is + // choosing to use, out of the options in the m.key.verification.start + // message. + KeyAgreementProtocol KeyAgreementProtocol `json:"key_agreement_protocol"` + // MessageAuthenticationCode is the message authentication code the device + // is choosing to use, out of the options in the m.key.verification.start + // message. + MessageAuthenticationCode MACMethod `json:"message_authentication_code"` + // ShortAuthenticationString is a list of SAS methods both devices involved + // in the verification process understand. Must be a subset of the options + // in the m.key.verification.start message. + ShortAuthenticationString []SASMethod `json:"short_authentication_string"` +} + +// VerificationKeyEventContent represents the content of an +// [m.key.verification.key] event (both the to-device and the in-room version) +// as described in [Section 11.12.2.2.2] of the Spec. +// +// [m.key.verification.key]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationkey +// [Section 11.12.2.2.2]: https://spec.matrix.org/v1.9/client-server-api/#verification-messages-specific-to-sas +type VerificationKeyEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent + + // Key is the device’s ephemeral public key. + Key jsonbytes.UnpaddedBytes `json:"key"` +} + +// VerificationMACEventContent represents the content of an +// [m.key.verification.mac] event (both the to-device and the in-room version) +// as described in [Section 11.12.2.2.2] of the Spec. +// +// [m.key.verification.mac]: https://spec.matrix.org/v1.9/client-server-api/#mkeyverificationmac +// [Section 11.12.2.2.2]: https://spec.matrix.org/v1.9/client-server-api/#verification-messages-specific-to-sas +type VerificationMACEventContent struct { + ToDeviceVerificationEvent + InRoomVerificationEvent + + // Keys is the MAC of the comma-separated, sorted, list of key IDs given in + // the MAC property. + Keys jsonbytes.UnpaddedBytes `json:"keys"` + // MAC is a map of the key ID to the MAC of the key, using the algorithm in + // the verification process. + MAC map[id.KeyID]jsonbytes.UnpaddedBytes `json:"mac"` +} diff --git a/vendor/maunium.net/go/mautrix/event/voip.go b/vendor/maunium.net/go/mautrix/event/voip.go new file mode 100644 index 00000000..cd8364a1 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/event/voip.go @@ -0,0 +1,116 @@ +// Copyright (c) 2021 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package event + +import ( + "encoding/json" + "fmt" + "strconv" +) + +type CallHangupReason string + +const ( + CallHangupICEFailed CallHangupReason = "ice_failed" + CallHangupInviteTimeout CallHangupReason = "invite_timeout" + CallHangupUserHangup CallHangupReason = "user_hangup" + CallHangupUserMediaFailed CallHangupReason = "user_media_failed" + CallHangupUnknownError CallHangupReason = "unknown_error" +) + +type CallDataType string + +const ( + CallDataTypeOffer CallDataType = "offer" + CallDataTypeAnswer CallDataType = "answer" +) + +type CallData struct { + SDP string `json:"sdp"` + Type CallDataType `json:"type"` +} + +type CallCandidate struct { + Candidate string `json:"candidate"` + SDPMLineIndex int `json:"sdpMLineIndex"` + SDPMID string `json:"sdpMid"` +} + +type CallVersion string + +func (cv *CallVersion) UnmarshalJSON(raw []byte) error { + var numberVersion int + err := json.Unmarshal(raw, &numberVersion) + if err != nil { + var stringVersion string + err = json.Unmarshal(raw, &stringVersion) + if err != nil { + return fmt.Errorf("failed to parse CallVersion: %w", err) + } + *cv = CallVersion(stringVersion) + } else { + *cv = CallVersion(strconv.Itoa(numberVersion)) + } + return nil +} + +func (cv *CallVersion) MarshalJSON() ([]byte, error) { + for _, char := range *cv { + if char < '0' || char > '9' { + // The version contains weird characters, return as string. + return json.Marshal(string(*cv)) + } + } + // The version consists of only ASCII digits, return as an integer. + return []byte(*cv), nil +} + +func (cv *CallVersion) Int() (int, error) { + return strconv.Atoi(string(*cv)) +} + +type BaseCallEventContent struct { + CallID string `json:"call_id"` + PartyID string `json:"party_id"` + Version CallVersion `json:"version,omitempty"` +} + +type CallInviteEventContent struct { + BaseCallEventContent + Lifetime int `json:"lifetime"` + Offer CallData `json:"offer"` +} + +type CallCandidatesEventContent struct { + BaseCallEventContent + Candidates []CallCandidate `json:"candidates"` +} + +type CallRejectEventContent struct { + BaseCallEventContent +} + +type CallAnswerEventContent struct { + BaseCallEventContent + Answer CallData `json:"answer"` +} + +type CallSelectAnswerEventContent struct { + BaseCallEventContent + SelectedPartyID string `json:"selected_party_id"` +} + +type CallNegotiateEventContent struct { + BaseCallEventContent + Lifetime int `json:"lifetime"` + Description CallData `json:"description"` +} + +type CallHangupEventContent struct { + BaseCallEventContent + Reason CallHangupReason `json:"reason"` +} diff --git a/vendor/maunium.net/go/mautrix/filter.go b/vendor/maunium.net/go/mautrix/filter.go new file mode 100644 index 00000000..c6c8211b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/filter.go @@ -0,0 +1,95 @@ +// Copyright 2017 Jan Christian Grünhage + +package mautrix + +import ( + "errors" + + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +type EventFormat string + +const ( + EventFormatClient EventFormat = "client" + EventFormatFederation EventFormat = "federation" +) + +// Filter is used by clients to specify how the server should filter responses to e.g. sync requests +// Specified by: https://spec.matrix.org/v1.2/client-server-api/#filtering +type Filter struct { + AccountData *FilterPart `json:"account_data,omitempty"` + EventFields []string `json:"event_fields,omitempty"` + EventFormat EventFormat `json:"event_format,omitempty"` + Presence *FilterPart `json:"presence,omitempty"` + Room *RoomFilter `json:"room,omitempty"` + + BeeperToDevice *FilterPart `json:"com.beeper.to_device,omitempty"` +} + +// RoomFilter is used to define filtering rules for room events +type RoomFilter struct { + AccountData *FilterPart `json:"account_data,omitempty"` + Ephemeral *FilterPart `json:"ephemeral,omitempty"` + IncludeLeave bool `json:"include_leave,omitempty"` + NotRooms []id.RoomID `json:"not_rooms,omitempty"` + Rooms []id.RoomID `json:"rooms,omitempty"` + State *FilterPart `json:"state,omitempty"` + Timeline *FilterPart `json:"timeline,omitempty"` +} + +// FilterPart is used to define filtering rules for specific categories of events +type FilterPart struct { + NotRooms []id.RoomID `json:"not_rooms,omitempty"` + Rooms []id.RoomID `json:"rooms,omitempty"` + Limit int `json:"limit,omitempty"` + NotSenders []id.UserID `json:"not_senders,omitempty"` + NotTypes []event.Type `json:"not_types,omitempty"` + Senders []id.UserID `json:"senders,omitempty"` + Types []event.Type `json:"types,omitempty"` + ContainsURL *bool `json:"contains_url,omitempty"` + LazyLoadMembers bool `json:"lazy_load_members,omitempty"` + IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"` + UnreadThreadNotifications bool `json:"unread_thread_notifications,omitempty"` +} + +// Validate checks if the filter contains valid property values +func (filter *Filter) Validate() error { + if filter.EventFormat != EventFormatClient && filter.EventFormat != EventFormatFederation { + return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]") + } + return nil +} + +// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request +func DefaultFilter() Filter { + return Filter{ + AccountData: DefaultFilterPart(), + EventFields: nil, + EventFormat: "client", + Presence: DefaultFilterPart(), + Room: &RoomFilter{ + AccountData: DefaultFilterPart(), + Ephemeral: DefaultFilterPart(), + IncludeLeave: false, + NotRooms: nil, + Rooms: nil, + State: DefaultFilterPart(), + Timeline: DefaultFilterPart(), + }, + } +} + +// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request +func DefaultFilterPart() *FilterPart { + return &FilterPart{ + NotRooms: nil, + Rooms: nil, + Limit: 20, + NotSenders: nil, + NotTypes: nil, + Senders: nil, + Types: nil, + } +} diff --git a/vendor/maunium.net/go/mautrix/id/contenturi.go b/vendor/maunium.net/go/mautrix/id/contenturi.go new file mode 100644 index 00000000..e6a313f5 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/contenturi.go @@ -0,0 +1,177 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "regexp" + "strings" +) + +var ( + InvalidContentURI = errors.New("invalid Matrix content URI") + InputNotJSONString = errors.New("input doesn't look like a JSON string") +) + +// ContentURIString is a string that's expected to be a Matrix content URI. +// It's useful for delaying the parsing of the content URI to move errors from the event content +// JSON parsing step to a later step where more appropriate errors can be produced. +type ContentURIString string + +func (uriString ContentURIString) Parse() (ContentURI, error) { + return ParseContentURI(string(uriString)) +} + +func (uriString ContentURIString) ParseOrIgnore() ContentURI { + parsed, _ := ParseContentURI(string(uriString)) + return parsed +} + +// ContentURI represents a Matrix content URI. +// https://spec.matrix.org/v1.2/client-server-api/#matrix-content-mxc-uris +type ContentURI struct { + Homeserver string + FileID string +} + +func MustParseContentURI(uri string) ContentURI { + parsed, err := ParseContentURI(uri) + if err != nil { + panic(err) + } + return parsed +} + +// ParseContentURI parses a Matrix content URI. +func ParseContentURI(uri string) (parsed ContentURI, err error) { + if len(uri) == 0 { + return + } else if !strings.HasPrefix(uri, "mxc://") { + err = InvalidContentURI + } else if index := strings.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 { + err = InvalidContentURI + } else { + parsed.Homeserver = uri[6 : 6+index] + parsed.FileID = uri[6+index+1:] + } + return +} + +var mxcBytes = []byte("mxc://") + +func ParseContentURIBytes(uri []byte) (parsed ContentURI, err error) { + if len(uri) == 0 { + return + } else if !bytes.HasPrefix(uri, mxcBytes) { + err = InvalidContentURI + } else if index := bytes.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 { + err = InvalidContentURI + } else { + parsed.Homeserver = string(uri[6 : 6+index]) + parsed.FileID = string(uri[6+index+1:]) + } + return +} + +func (uri *ContentURI) UnmarshalJSON(raw []byte) (err error) { + if string(raw) == "null" { + *uri = ContentURI{} + return nil + } else if len(raw) < 2 || raw[0] != '"' || raw[len(raw)-1] != '"' { + return InputNotJSONString + } + parsed, err := ParseContentURIBytes(raw[1 : len(raw)-1]) + if err != nil { + return err + } + *uri = parsed + return nil +} + +func (uri *ContentURI) MarshalJSON() ([]byte, error) { + if uri == nil || uri.IsEmpty() { + return []byte("null"), nil + } + return json.Marshal(uri.String()) +} + +func (uri *ContentURI) UnmarshalText(raw []byte) (err error) { + parsed, err := ParseContentURIBytes(raw) + if err != nil { + return err + } + *uri = parsed + return nil +} + +func (uri ContentURI) MarshalText() ([]byte, error) { + return []byte(uri.String()), nil +} + +func (uri *ContentURI) Scan(i interface{}) error { + var parsed ContentURI + var err error + switch value := i.(type) { + case nil: + // don't do anything, set uri to empty + case []byte: + parsed, err = ParseContentURIBytes(value) + case string: + parsed, err = ParseContentURI(value) + default: + return fmt.Errorf("invalid type %T for ContentURI.Scan", i) + } + if err != nil { + return err + } + *uri = parsed + return nil +} + +func (uri *ContentURI) Value() (driver.Value, error) { + if uri == nil { + return nil, nil + } + return uri.String(), nil +} + +func (uri ContentURI) String() string { + if uri.IsEmpty() { + return "" + } + return fmt.Sprintf("mxc://%s/%s", uri.Homeserver, uri.FileID) +} + +func (uri ContentURI) CUString() ContentURIString { + return ContentURIString(uri.String()) +} + +func (uri ContentURI) IsEmpty() bool { + return len(uri.Homeserver) == 0 || len(uri.FileID) == 0 +} + +var simpleHomeserverRegex = regexp.MustCompile(`^[a-zA-Z0-9.:-]+$`) + +func (uri ContentURI) IsValid() bool { + return IsValidMediaID(uri.FileID) && uri.Homeserver != "" && simpleHomeserverRegex.MatchString(uri.Homeserver) +} + +func IsValidMediaID(mediaID string) bool { + if len(mediaID) == 0 { + return false + } + for _, char := range mediaID { + if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') && char != '-' && char != '_' { + return false + } + } + return true +} diff --git a/vendor/maunium.net/go/mautrix/id/crypto.go b/vendor/maunium.net/go/mautrix/id/crypto.go new file mode 100644 index 00000000..355a84a8 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/crypto.go @@ -0,0 +1,203 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "encoding/base64" + "fmt" + "strings" + + "go.mau.fi/util/random" +) + +// OlmMsgType is an Olm message type +type OlmMsgType int + +const ( + OlmMsgTypePreKey OlmMsgType = 0 + OlmMsgTypeMsg OlmMsgType = 1 +) + +// Algorithm is a Matrix message encryption algorithm. +// https://spec.matrix.org/v1.2/client-server-api/#messaging-algorithm-names +type Algorithm string + +const ( + AlgorithmOlmV1 Algorithm = "m.olm.v1.curve25519-aes-sha2" + AlgorithmMegolmV1 Algorithm = "m.megolm.v1.aes-sha2" +) + +type KeyAlgorithm string + +const ( + KeyAlgorithmCurve25519 KeyAlgorithm = "curve25519" + KeyAlgorithmEd25519 KeyAlgorithm = "ed25519" + KeyAlgorithmSignedCurve25519 KeyAlgorithm = "signed_curve25519" +) + +type CrossSigningUsage string + +const ( + XSUsageMaster CrossSigningUsage = "master" + XSUsageSelfSigning CrossSigningUsage = "self_signing" + XSUsageUserSigning CrossSigningUsage = "user_signing" +) + +type KeyBackupAlgorithm string + +const ( + KeyBackupAlgorithmMegolmBackupV1 KeyBackupAlgorithm = "m.megolm_backup.v1.curve25519-aes-sha2" +) + +// BackupVersion is an arbitrary string that identifies a server side key backup. +type KeyBackupVersion string + +func (version KeyBackupVersion) String() string { + return string(version) +} + +// A SessionID is an arbitrary string that identifies an Olm or Megolm session. +type SessionID string + +func (sessionID SessionID) String() string { + return string(sessionID) +} + +// Ed25519 is the base64 representation of an Ed25519 public key +type Ed25519 string +type SigningKey = Ed25519 + +func (ed25519 Ed25519) String() string { + return string(ed25519) +} + +func (ed25519 Ed25519) Bytes() []byte { + val, _ := base64.RawStdEncoding.DecodeString(string(ed25519)) + // TODO handle errors + return val +} + +func (ed25519 Ed25519) Fingerprint() string { + spacedSigningKey := make([]byte, len(ed25519)+(len(ed25519)-1)/4) + var ptr = 0 + for i, chr := range ed25519 { + spacedSigningKey[ptr] = byte(chr) + ptr++ + if i%4 == 3 { + spacedSigningKey[ptr] = ' ' + ptr++ + } + } + return string(spacedSigningKey) +} + +// Curve25519 is the base64 representation of an Curve25519 public key +type Curve25519 string +type SenderKey = Curve25519 +type IdentityKey = Curve25519 + +func (curve25519 Curve25519) String() string { + return string(curve25519) +} + +func (curve25519 Curve25519) Bytes() []byte { + val, _ := base64.RawStdEncoding.DecodeString(string(curve25519)) + // TODO handle errors + return val +} + +// A DeviceID is an arbitrary string that references a specific device. +type DeviceID string + +func (deviceID DeviceID) String() string { + return string(deviceID) +} + +// A DeviceKeyID is a string formatted as : that is used as the key in deviceid-key mappings. +type DeviceKeyID string + +func NewDeviceKeyID(algorithm KeyAlgorithm, deviceID DeviceID) DeviceKeyID { + return DeviceKeyID(fmt.Sprintf("%s:%s", algorithm, deviceID)) +} + +func (deviceKeyID DeviceKeyID) String() string { + return string(deviceKeyID) +} + +func (deviceKeyID DeviceKeyID) Parse() (Algorithm, DeviceID) { + index := strings.IndexRune(string(deviceKeyID), ':') + if index < 0 || len(deviceKeyID) <= index+1 { + return "", "" + } + return Algorithm(deviceKeyID[:index]), DeviceID(deviceKeyID[index+1:]) +} + +// A KeyID a string formatted as : that is used as the key in one-time-key mappings. +type KeyID string + +func NewKeyID(algorithm KeyAlgorithm, keyID string) KeyID { + return KeyID(fmt.Sprintf("%s:%s", algorithm, keyID)) +} + +func (keyID KeyID) String() string { + return string(keyID) +} + +func (keyID KeyID) Parse() (KeyAlgorithm, string) { + index := strings.IndexRune(string(keyID), ':') + if index < 0 || len(keyID) <= index+1 { + return "", "" + } + return KeyAlgorithm(keyID[:index]), string(keyID[index+1:]) +} + +// Device contains the identity details of a device and some additional info. +type Device struct { + UserID UserID + DeviceID DeviceID + IdentityKey Curve25519 + SigningKey Ed25519 + + Trust TrustState + Deleted bool + Name string +} + +func (device *Device) Fingerprint() string { + return device.SigningKey.Fingerprint() +} + +type CrossSigningKey struct { + Key Ed25519 + First Ed25519 +} + +// Secret storage keys +type Secret string + +func (s Secret) String() string { + return string(s) +} + +const ( + SecretXSMaster Secret = "m.cross_signing.master" + SecretXSSelfSigning Secret = "m.cross_signing.self_signing" + SecretXSUserSigning Secret = "m.cross_signing.user_signing" + SecretMegolmBackupV1 Secret = "m.megolm_backup.v1" +) + +// VerificationTransactionID is a unique identifier for a verification +// transaction. +type VerificationTransactionID string + +func NewVerificationTransactionID() VerificationTransactionID { + return VerificationTransactionID(random.String(32)) +} + +func (t VerificationTransactionID) String() string { + return string(t) +} diff --git a/vendor/maunium.net/go/mautrix/id/matrixuri.go b/vendor/maunium.net/go/mautrix/id/matrixuri.go new file mode 100644 index 00000000..8f5ec849 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/matrixuri.go @@ -0,0 +1,309 @@ +// Copyright (c) 2021 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "errors" + "fmt" + "net/url" + "strings" +) + +// Errors that can happen when parsing matrix: URIs +var ( + ErrInvalidScheme = errors.New("matrix URI scheme must be exactly 'matrix'") + ErrInvalidPartCount = errors.New("matrix URIs must have exactly 2 or 4 segments") + ErrInvalidFirstSegment = errors.New("invalid identifier in first segment of matrix URI") + ErrEmptySecondSegment = errors.New("the second segment of the matrix URI must not be empty") + ErrInvalidThirdSegment = errors.New("invalid identifier in third segment of matrix URI") + ErrEmptyFourthSegment = errors.New("the fourth segment of the matrix URI must not be empty when the third segment is present") +) + +// Errors that can happen when parsing matrix.to URLs +var ( + ErrNotMatrixTo = errors.New("that URL is not a matrix.to URL") + ErrInvalidMatrixToPartCount = errors.New("matrix.to URLs must have exactly 1 or 2 segments") + ErrEmptyMatrixToPrimaryIdentifier = errors.New("the primary identifier in the matrix.to URL is empty") + ErrInvalidMatrixToPrimaryIdentifier = errors.New("the primary identifier in the matrix.to URL has an invalid sigil") + ErrInvalidMatrixToSecondaryIdentifier = errors.New("the secondary identifier in the matrix.to URL has an invalid sigil") +) + +var ErrNotMatrixToOrMatrixURI = errors.New("that URL is not a matrix.to URL nor matrix: URI") + +// MatrixURI contains the result of parsing a matrix: URI using ParseMatrixURI +type MatrixURI struct { + Sigil1 rune + Sigil2 rune + MXID1 string + MXID2 string + Via []string + Action string +} + +// SigilToPathSegment contains a mapping from Matrix identifier sigils to matrix: URI path segments. +var SigilToPathSegment = map[rune]string{ + '$': "e", + '#': "r", + '!': "roomid", + '@': "u", +} + +func (uri *MatrixURI) getQuery() url.Values { + q := make(url.Values) + if uri.Via != nil && len(uri.Via) > 0 { + q["via"] = uri.Via + } + if len(uri.Action) > 0 { + q.Set("action", uri.Action) + } + return q +} + +// String converts the parsed matrix: URI back into the string representation. +func (uri *MatrixURI) String() string { + if uri == nil { + return "" + } + parts := []string{ + SigilToPathSegment[uri.Sigil1], + url.PathEscape(uri.MXID1), + } + if uri.Sigil2 != 0 { + parts = append(parts, SigilToPathSegment[uri.Sigil2], url.PathEscape(uri.MXID2)) + } + return (&url.URL{ + Scheme: "matrix", + Opaque: strings.Join(parts, "/"), + RawQuery: uri.getQuery().Encode(), + }).String() +} + +// MatrixToURL converts to parsed matrix: URI into a matrix.to URL +func (uri *MatrixURI) MatrixToURL() string { + if uri == nil { + return "" + } + fragment := fmt.Sprintf("#/%s", url.PathEscape(uri.PrimaryIdentifier())) + if uri.Sigil2 != 0 { + fragment = fmt.Sprintf("%s/%s", fragment, url.PathEscape(uri.SecondaryIdentifier())) + } + query := uri.getQuery().Encode() + if len(query) > 0 { + fragment = fmt.Sprintf("%s?%s", fragment, query) + } + // It would be nice to use URL{...}.String() here, but figuring out the Fragment vs RawFragment stuff is a pain + return fmt.Sprintf("https://matrix.to/%s", fragment) +} + +// PrimaryIdentifier returns the first Matrix identifier in the URI. +// Currently room IDs, room aliases and user IDs can be in the primary identifier slot. +func (uri *MatrixURI) PrimaryIdentifier() string { + if uri == nil { + return "" + } + return fmt.Sprintf("%c%s", uri.Sigil1, uri.MXID1) +} + +// SecondaryIdentifier returns the second Matrix identifier in the URI. +// Currently only event IDs can be in the secondary identifier slot. +func (uri *MatrixURI) SecondaryIdentifier() string { + if uri == nil || uri.Sigil2 == 0 { + return "" + } + return fmt.Sprintf("%c%s", uri.Sigil2, uri.MXID2) +} + +// UserID returns the user ID from the URI if the primary identifier is a user ID. +func (uri *MatrixURI) UserID() UserID { + if uri != nil && uri.Sigil1 == '@' { + return UserID(uri.PrimaryIdentifier()) + } + return "" +} + +// RoomID returns the room ID from the URI if the primary identifier is a room ID. +func (uri *MatrixURI) RoomID() RoomID { + if uri != nil && uri.Sigil1 == '!' { + return RoomID(uri.PrimaryIdentifier()) + } + return "" +} + +// RoomAlias returns the room alias from the URI if the primary identifier is a room alias. +func (uri *MatrixURI) RoomAlias() RoomAlias { + if uri != nil && uri.Sigil1 == '#' { + return RoomAlias(uri.PrimaryIdentifier()) + } + return "" +} + +// EventID returns the event ID from the URI if the primary identifier is a room ID or alias and the secondary identifier is an event ID. +func (uri *MatrixURI) EventID() EventID { + if uri != nil && (uri.Sigil1 == '!' || uri.Sigil1 == '#') && uri.Sigil2 == '$' { + return EventID(uri.SecondaryIdentifier()) + } + return "" +} + +// ParseMatrixURIOrMatrixToURL parses the given matrix.to URL or matrix: URI into a unified representation. +func ParseMatrixURIOrMatrixToURL(uri string) (*MatrixURI, error) { + parsed, err := url.Parse(uri) + if err != nil { + return nil, fmt.Errorf("failed to parse URI: %w", err) + } + if parsed.Scheme == "matrix" { + return ProcessMatrixURI(parsed) + } else if strings.HasSuffix(parsed.Hostname(), "matrix.to") { + return ProcessMatrixToURL(parsed) + } else { + return nil, ErrNotMatrixToOrMatrixURI + } +} + +// ParseMatrixURI implements the matrix: URI parsing algorithm. +// +// Currently specified in https://github.com/matrix-org/matrix-doc/blob/master/proposals/2312-matrix-uri.md#uri-parsing-algorithm +func ParseMatrixURI(uri string) (*MatrixURI, error) { + // Step 1: parse the URI according to RFC 3986 + parsed, err := url.Parse(uri) + if err != nil { + return nil, fmt.Errorf("failed to parse URI: %w", err) + } + return ProcessMatrixURI(parsed) +} + +// ProcessMatrixURI implements steps 2-7 of the matrix: URI parsing algorithm +// (i.e. everything except parsing the URI itself, which is done with url.Parse or ParseMatrixURI) +func ProcessMatrixURI(uri *url.URL) (*MatrixURI, error) { + // Step 2: check that scheme is exactly `matrix` + if uri.Scheme != "matrix" { + return nil, ErrInvalidScheme + } + + // Step 3: split the path into segments separated by / + parts := strings.Split(uri.Opaque, "/") + + // Step 4: Check that the URI contains either 2 or 4 segments + if len(parts) != 2 && len(parts) != 4 { + return nil, ErrInvalidPartCount + } + + var parsed MatrixURI + + // Step 5: Construct the top-level Matrix identifier + // a: find the sigil from the first segment + switch parts[0] { + case "u", "user": + parsed.Sigil1 = '@' + case "r", "room": + parsed.Sigil1 = '#' + case "roomid": + parsed.Sigil1 = '!' + default: + return nil, fmt.Errorf("%w: '%s'", ErrInvalidFirstSegment, parts[0]) + } + // b: find the identifier from the second segment + if len(parts[1]) == 0 { + return nil, ErrEmptySecondSegment + } + var err error + parsed.MXID1, err = url.PathUnescape(parts[1]) + if err != nil { + return nil, fmt.Errorf("failed to url decode second segment %q: %w", parts[1], err) + } + + // Step 6: if the first part is a room and the URI has 4 segments, construct a second level identifier + if parsed.Sigil1 == '!' && len(parts) == 4 { + // a: find the sigil from the third segment + switch parts[2] { + case "e", "event": + parsed.Sigil2 = '$' + default: + return nil, fmt.Errorf("%w: '%s'", ErrInvalidThirdSegment, parts[0]) + } + + // b: find the identifier from the fourth segment + if len(parts[3]) == 0 { + return nil, ErrEmptyFourthSegment + } + parsed.MXID2, err = url.PathUnescape(parts[3]) + if err != nil { + return nil, fmt.Errorf("failed to url decode fourth segment %q: %w", parts[3], err) + } + } + + // Step 7: parse the query and extract via and action items + via, ok := uri.Query()["via"] + if ok && len(via) > 0 { + parsed.Via = via + } + action, ok := uri.Query()["action"] + if ok && len(action) > 0 { + parsed.Action = action[len(action)-1] + } + + return &parsed, nil +} + +// ParseMatrixToURL parses a matrix.to URL into the same container as ParseMatrixURI parses matrix: URIs. +func ParseMatrixToURL(uri string) (*MatrixURI, error) { + parsed, err := url.Parse(uri) + if err != nil { + return nil, fmt.Errorf("failed to parse URL: %w", err) + } + return ProcessMatrixToURL(parsed) +} + +// ProcessMatrixToURL is the equivalent of ProcessMatrixURI for matrix.to URLs. +func ProcessMatrixToURL(uri *url.URL) (*MatrixURI, error) { + if !strings.HasSuffix(uri.Hostname(), "matrix.to") { + return nil, ErrNotMatrixTo + } + + initialSplit := strings.SplitN(uri.Fragment, "?", 2) + parts := strings.Split(initialSplit[0], "/") + if len(initialSplit) > 1 { + uri.RawQuery = initialSplit[1] + } + + if len(parts) < 2 || len(parts) > 3 { + return nil, ErrInvalidMatrixToPartCount + } + + if len(parts[1]) == 0 { + return nil, ErrEmptyMatrixToPrimaryIdentifier + } + + var parsed MatrixURI + + parsed.Sigil1 = rune(parts[1][0]) + parsed.MXID1 = parts[1][1:] + _, isKnown := SigilToPathSegment[parsed.Sigil1] + if !isKnown { + return nil, ErrInvalidMatrixToPrimaryIdentifier + } + + if len(parts) == 3 && len(parts[2]) > 0 { + parsed.Sigil2 = rune(parts[2][0]) + parsed.MXID2 = parts[2][1:] + _, isKnown = SigilToPathSegment[parsed.Sigil2] + if !isKnown { + return nil, ErrInvalidMatrixToSecondaryIdentifier + } + } + + via, ok := uri.Query()["via"] + if ok && len(via) > 0 { + parsed.Via = via + } + action, ok := uri.Query()["action"] + if ok && len(action) > 0 { + parsed.Action = action[len(action)-1] + } + + return &parsed, nil +} diff --git a/vendor/maunium.net/go/mautrix/id/opaque.go b/vendor/maunium.net/go/mautrix/id/opaque.go new file mode 100644 index 00000000..c1ad4988 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/opaque.go @@ -0,0 +1,101 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "fmt" +) + +// A RoomID is a string starting with ! that references a specific room. +// https://matrix.org/docs/spec/appendices#room-ids-and-event-ids +type RoomID string + +// A RoomAlias is a string starting with # that can be resolved into. +// https://matrix.org/docs/spec/appendices#room-aliases +type RoomAlias string + +func NewRoomAlias(localpart, server string) RoomAlias { + return RoomAlias(fmt.Sprintf("#%s:%s", localpart, server)) +} + +// An EventID is a string starting with $ that references a specific event. +// +// https://matrix.org/docs/spec/appendices#room-ids-and-event-ids +// https://matrix.org/docs/spec/rooms/v4#event-ids +type EventID string + +// A BatchID is a string identifying a batch of events being backfilled to a room. +// https://github.com/matrix-org/matrix-doc/pull/2716 +type BatchID string + +// A DelayID is a string identifying a delayed event. +type DelayID string + +func (roomID RoomID) String() string { + return string(roomID) +} + +func (roomID RoomID) URI(via ...string) *MatrixURI { + if roomID == "" { + return nil + } + return &MatrixURI{ + Sigil1: '!', + MXID1: string(roomID)[1:], + Via: via, + } +} + +func (roomID RoomID) EventURI(eventID EventID, via ...string) *MatrixURI { + if roomID == "" { + return nil + } else if eventID == "" { + return roomID.URI(via...) + } + return &MatrixURI{ + Sigil1: '!', + MXID1: string(roomID)[1:], + Sigil2: '$', + MXID2: string(eventID)[1:], + Via: via, + } +} + +func (roomAlias RoomAlias) String() string { + return string(roomAlias) +} + +func (roomAlias RoomAlias) URI() *MatrixURI { + if roomAlias == "" { + return nil + } + return &MatrixURI{ + Sigil1: '#', + MXID1: string(roomAlias)[1:], + } +} + +// Deprecated: room alias event links should not be used. Use room IDs instead. +func (roomAlias RoomAlias) EventURI(eventID EventID) *MatrixURI { + if roomAlias == "" { + return nil + } + return &MatrixURI{ + Sigil1: '#', + MXID1: string(roomAlias)[1:], + Sigil2: '$', + MXID2: string(eventID)[1:], + } +} + +func (eventID EventID) String() string { + return string(eventID) +} + +func (batchID BatchID) String() string { + return string(batchID) +} diff --git a/vendor/maunium.net/go/mautrix/id/roomversion.go b/vendor/maunium.net/go/mautrix/id/roomversion.go new file mode 100644 index 00000000..578c10bd --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/roomversion.go @@ -0,0 +1,265 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "errors" + "fmt" + "slices" +) + +type RoomVersion string + +const ( + RoomV0 RoomVersion = "" // No room version, used for rooms created before room versions were introduced, equivalent to v1 + RoomV1 RoomVersion = "1" + RoomV2 RoomVersion = "2" + RoomV3 RoomVersion = "3" + RoomV4 RoomVersion = "4" + RoomV5 RoomVersion = "5" + RoomV6 RoomVersion = "6" + RoomV7 RoomVersion = "7" + RoomV8 RoomVersion = "8" + RoomV9 RoomVersion = "9" + RoomV10 RoomVersion = "10" + RoomV11 RoomVersion = "11" + RoomV12 RoomVersion = "12" +) + +func (rv RoomVersion) Equals(versions ...RoomVersion) bool { + return slices.Contains(versions, rv) +} + +func (rv RoomVersion) NotEquals(versions ...RoomVersion) bool { + return !rv.Equals(versions...) +} + +var ErrUnknownRoomVersion = errors.New("unknown room version") + +func (rv RoomVersion) unknownVersionError() error { + return fmt.Errorf("%w %s", ErrUnknownRoomVersion, rv) +} + +func (rv RoomVersion) IsKnown() bool { + switch rv { + case RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11, RoomV12: + return true + default: + return false + } +} + +type StateResVersion int + +const ( + // StateResV1 is the original state resolution algorithm. + StateResV1 StateResVersion = 0 + // StateResV2 is state resolution v2 introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/1759 + StateResV2 StateResVersion = 1 + // StateResV2_1 is state resolution v2.1 introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/4297 + StateResV2_1 StateResVersion = 2 +) + +// StateResVersion returns the version of the state resolution algorithm used by this room version. +func (rv RoomVersion) StateResVersion() StateResVersion { + switch rv { + case RoomV0, RoomV1: + return StateResV1 + case RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11: + return StateResV2 + case RoomV12: + return StateResV2_1 + default: + panic(rv.unknownVersionError()) + } +} + +type EventIDFormat int + +const ( + // EventIDFormatCustom is the original format used by room v1 and v2. + // Event IDs in this format are an arbitrary string followed by a colon and the server name. + EventIDFormatCustom EventIDFormat = 0 + // EventIDFormatBase64 is the format used by room v3 introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/1659. + // Event IDs in this format are the standard unpadded base64-encoded SHA256 reference hash of the event. + EventIDFormatBase64 EventIDFormat = 1 + // EventIDFormatURLSafeBase64 is the format used by room v4 and later introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/2002. + // Event IDs in this format are the url-safe unpadded base64-encoded SHA256 reference hash of the event. + EventIDFormatURLSafeBase64 EventIDFormat = 2 +) + +// EventIDFormat returns the format of event IDs used by this room version. +func (rv RoomVersion) EventIDFormat() EventIDFormat { + switch rv { + case RoomV0, RoomV1, RoomV2: + return EventIDFormatCustom + case RoomV3: + return EventIDFormatBase64 + default: + return EventIDFormatURLSafeBase64 + } +} + +///////////////////// +// Room v5 changes // +///////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/2077 + +// EnforceSigningKeyValidity returns true if the `valid_until_ts` field of federation signing keys +// must be enforced on received events. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2076 +func (rv RoomVersion) EnforceSigningKeyValidity() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4) +} + +///////////////////// +// Room v6 changes // +///////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/2240 + +// SpecialCasedAliasesAuth returns true if the `m.room.aliases` event authorization is special cased +// to only always allow servers to modify the state event with their own server name as state key. +// This also implies that the `aliases` field is protected from redactions. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2432 +func (rv RoomVersion) SpecialCasedAliasesAuth() bool { + return rv.Equals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5) +} + +// ForbidFloatsAndBigInts returns true if floats and integers greater than 2^53-1 or lower than -2^53+1 are forbidden everywhere. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2540 +func (rv RoomVersion) ForbidFloatsAndBigInts() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5) +} + +// NotificationsPowerLevels returns true if the `notifications` field in `m.room.power_levels` is validated in event auth. +// However, the field is not protected from redactions. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2209 +func (rv RoomVersion) NotificationsPowerLevels() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5) +} + +///////////////////// +// Room v7 changes // +///////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/2998 + +// Knocks returns true if the `knock` join rule is supported. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2403 +func (rv RoomVersion) Knocks() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6) +} + +///////////////////// +// Room v8 changes // +///////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/3289 + +// RestrictedJoins returns true if the `restricted` join rule is supported. +// This also implies that the `allow` field in the `m.room.join_rules` event is supported and protected from redactions. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/3083 +func (rv RoomVersion) RestrictedJoins() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7) +} + +///////////////////// +// Room v9 changes // +///////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/3375 + +// RestrictedJoinsFix returns true if the `join_authorised_via_users_server` field in `m.room.member` events is protected from redactions. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/3375 +func (rv RoomVersion) RestrictedJoinsFix() bool { + return rv.RestrictedJoins() && rv != RoomV8 +} + +////////////////////// +// Room v10 changes // +////////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/3604 + +// ValidatePowerLevelInts returns true if the known values in `m.room.power_levels` must be integers (and not strings). +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/3667 +func (rv RoomVersion) ValidatePowerLevelInts() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9) +} + +// KnockRestricted returns true if the `knock_restricted` join rule is supported. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/3787 +func (rv RoomVersion) KnockRestricted() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9) +} + +////////////////////// +// Room v11 changes // +////////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/3820 + +// CreatorInContent returns true if the `m.room.create` event has a `creator` field in content. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2175 +func (rv RoomVersion) CreatorInContent() bool { + return rv.Equals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10) +} + +// RedactsInContent returns true if the `m.room.redaction` event has the `redacts` field in content instead of at the top level. +// The redaction protection is also moved from the top level to the content field. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2174 +// (and https://github.com/matrix-org/matrix-spec-proposals/pull/2176 for the redaction protection). +func (rv RoomVersion) RedactsInContent() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10) +} + +// UpdatedRedactionRules returns true if various updates to the redaction algorithm are applied. +// +// Specifically: +// +// * the `membership`, `origin`, and `prev_state` fields at the top level of all events are no longer protected. +// * the entire content of `m.room.create` is protected. +// * the `redacts` field in `m.room.redaction` content is protected instead of the top-level field. +// * the `m.room.power_levels` event protects the `invite` field in content. +// * the `signed` field inside the `third_party_invite` field in content of `m.room.member` events is protected. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/2176, +// https://github.com/matrix-org/matrix-spec-proposals/pull/3821, and +// https://github.com/matrix-org/matrix-spec-proposals/pull/3989 +func (rv RoomVersion) UpdatedRedactionRules() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10) +} + +////////////////////// +// Room v12 changes // +////////////////////// +// https://github.com/matrix-org/matrix-spec-proposals/pull/4304 + +// Return value of StateResVersion was changed to StateResV2_1 + +// PrivilegedRoomCreators returns true if the creator(s) of a room always have infinite power level. +// This also implies that the `m.room.create` event has an `additional_creators` field, +// and that the creators can't be present in the `m.room.power_levels` event. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/4289 +func (rv RoomVersion) PrivilegedRoomCreators() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11) +} + +// RoomIDIsCreateEventID returns true if the ID of rooms is the same as the ID of the `m.room.create` event. +// This also implies that `m.room.create` events do not have a `room_id` field. +// +// See https://github.com/matrix-org/matrix-spec-proposals/pull/4291 +func (rv RoomVersion) RoomIDIsCreateEventID() bool { + return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11) +} diff --git a/vendor/maunium.net/go/mautrix/id/servername.go b/vendor/maunium.net/go/mautrix/id/servername.go new file mode 100644 index 00000000..923705b6 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/servername.go @@ -0,0 +1,58 @@ +// Copyright (c) 2025 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "regexp" + "strconv" +) + +type ParsedServerNameType int + +const ( + ServerNameDNS ParsedServerNameType = iota + ServerNameIPv4 + ServerNameIPv6 +) + +type ParsedServerName struct { + Type ParsedServerNameType + Host string + Port int +} + +var ServerNameRegex = regexp.MustCompile(`^(?:\[([0-9A-Fa-f:.]{2,45})]|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([0-9A-Za-z.-]{1,255}))(?::(\d{1,5}))?$`) + +func ValidateServerName(serverName string) bool { + return len(serverName) <= 255 && len(serverName) > 0 && ServerNameRegex.MatchString(serverName) +} + +func ParseServerName(serverName string) *ParsedServerName { + if len(serverName) > 255 || len(serverName) < 1 { + return nil + } + match := ServerNameRegex.FindStringSubmatch(serverName) + if len(match) != 5 { + return nil + } + port, _ := strconv.Atoi(match[4]) + parsed := &ParsedServerName{ + Port: port, + } + switch { + case match[1] != "": + parsed.Type = ServerNameIPv6 + parsed.Host = match[1] + case match[2] != "": + parsed.Type = ServerNameIPv4 + parsed.Host = match[2] + case match[3] != "": + parsed.Type = ServerNameDNS + parsed.Host = match[3] + } + return parsed +} diff --git a/vendor/maunium.net/go/mautrix/id/trust.go b/vendor/maunium.net/go/mautrix/id/trust.go new file mode 100644 index 00000000..04f6e36b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/trust.go @@ -0,0 +1,87 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "fmt" + "strings" +) + +// TrustState determines how trusted a device is. +type TrustState int + +const ( + TrustStateBlacklisted TrustState = -100 + TrustStateUnset TrustState = 0 + TrustStateUnknownDevice TrustState = 10 + TrustStateForwarded TrustState = 20 + TrustStateCrossSignedUntrusted TrustState = 50 + TrustStateCrossSignedTOFU TrustState = 100 + TrustStateCrossSignedVerified TrustState = 200 + TrustStateVerified TrustState = 300 + TrustStateInvalid TrustState = (1 << 31) - 1 +) + +func (ts *TrustState) UnmarshalText(data []byte) error { + strData := string(data) + state := ParseTrustState(strData) + if state == TrustStateInvalid { + return fmt.Errorf("invalid trust state %q", strData) + } + *ts = state + return nil +} + +func (ts *TrustState) MarshalText() ([]byte, error) { + return []byte(ts.String()), nil +} + +func ParseTrustState(val string) TrustState { + switch strings.ToLower(val) { + case "blacklisted": + return TrustStateBlacklisted + case "unverified": + return TrustStateUnset + case "cross-signed-untrusted": + return TrustStateCrossSignedUntrusted + case "unknown-device": + return TrustStateUnknownDevice + case "forwarded": + return TrustStateForwarded + case "cross-signed-tofu", "cross-signed": + return TrustStateCrossSignedTOFU + case "cross-signed-verified", "cross-signed-trusted": + return TrustStateCrossSignedVerified + case "verified": + return TrustStateVerified + default: + return TrustStateInvalid + } +} + +func (ts TrustState) String() string { + switch ts { + case TrustStateBlacklisted: + return "blacklisted" + case TrustStateUnset: + return "unverified" + case TrustStateCrossSignedUntrusted: + return "cross-signed-untrusted" + case TrustStateUnknownDevice: + return "unknown-device" + case TrustStateForwarded: + return "forwarded" + case TrustStateCrossSignedTOFU: + return "cross-signed-tofu" + case TrustStateCrossSignedVerified: + return "cross-signed-verified" + case TrustStateVerified: + return "verified" + default: + return "invalid" + } +} diff --git a/vendor/maunium.net/go/mautrix/id/userid.go b/vendor/maunium.net/go/mautrix/id/userid.go new file mode 100644 index 00000000..859d2358 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/id/userid.go @@ -0,0 +1,254 @@ +// Copyright (c) 2021 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package id + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "regexp" + "strings" +) + +// UserID represents a Matrix user ID. +// https://matrix.org/docs/spec/appendices#user-identifiers +type UserID string + +const UserIDMaxLength = 255 + +func NewUserID(localpart, homeserver string) UserID { + return UserID(fmt.Sprintf("@%s:%s", localpart, homeserver)) +} + +func NewEncodedUserID(localpart, homeserver string) UserID { + return NewUserID(EncodeUserLocalpart(localpart), homeserver) +} + +var ( + ErrInvalidUserID = errors.New("is not a valid user ID") + ErrNoncompliantLocalpart = errors.New("contains characters that are not allowed") + ErrUserIDTooLong = errors.New("the given user ID is longer than 255 characters") + ErrEmptyLocalpart = errors.New("empty localparts are not allowed") + ErrNoncompliantServerPart = errors.New("is not a valid server name") +) + +// ParseCommonIdentifier parses a common identifier according to https://spec.matrix.org/v1.9/appendices/#common-identifier-format +func ParseCommonIdentifier[Stringish ~string](identifier Stringish) (sigil byte, localpart, homeserver string) { + if len(identifier) == 0 { + return + } + sigil = identifier[0] + strIdentifier := string(identifier) + colonIdx := strings.IndexByte(strIdentifier, ':') + if colonIdx > 0 { + localpart = strIdentifier[1:colonIdx] + homeserver = strIdentifier[colonIdx+1:] + } else { + localpart = strIdentifier[1:] + } + return +} + +// Parse parses the user ID into the localpart and server name. +// +// Note that this only enforces very basic user ID formatting requirements: user IDs start with +// a @, and contain a : after the @. If you want to enforce localpart validity, see the +// ParseAndValidate and ValidateUserLocalpart functions. +func (userID UserID) Parse() (localpart, homeserver string, err error) { + var sigil byte + sigil, localpart, homeserver = ParseCommonIdentifier(userID) + if sigil != '@' || homeserver == "" { + err = fmt.Errorf("'%s' %w", userID, ErrInvalidUserID) + } + return +} + +func (userID UserID) Localpart() string { + localpart, _, _ := userID.Parse() + return localpart +} + +func (userID UserID) Homeserver() string { + _, homeserver, _ := userID.Parse() + return homeserver +} + +// URI returns the user ID as a MatrixURI struct, which can then be stringified into a matrix: URI or a matrix.to URL. +// +// This does not parse or validate the user ID. Use the ParseAndValidate method if you want to ensure the user ID is valid first. +func (userID UserID) URI() *MatrixURI { + if userID == "" { + return nil + } + return &MatrixURI{ + Sigil1: '@', + MXID1: string(userID)[1:], + } +} + +var ValidLocalpartRegex = regexp.MustCompile("^[0-9a-z-.=_/+]+$") + +// ValidateUserLocalpart validates a Matrix user ID localpart using the grammar +// in https://matrix.org/docs/spec/appendices#user-identifier +func ValidateUserLocalpart(localpart string) error { + if len(localpart) == 0 { + return ErrEmptyLocalpart + } else if !ValidLocalpartRegex.MatchString(localpart) { + return fmt.Errorf("'%s' %w", localpart, ErrNoncompliantLocalpart) + } + return nil +} + +// ParseAndValidateStrict is a stricter version of ParseAndValidateRelaxed that checks the localpart to only allow non-historical localparts. +// This should be used with care: there are real users still using historical localparts. +func (userID UserID) ParseAndValidateStrict() (localpart, homeserver string, err error) { + localpart, homeserver, err = userID.ParseAndValidateRelaxed() + if err == nil { + err = ValidateUserLocalpart(localpart) + } + return +} + +// ParseAndValidateRelaxed parses the user ID into the localpart and server name like Parse, +// and also validates that the user ID is not too long and that the server name is valid. +func (userID UserID) ParseAndValidateRelaxed() (localpart, homeserver string, err error) { + if len(userID) > UserIDMaxLength { + err = ErrUserIDTooLong + return + } + localpart, homeserver, err = userID.Parse() + if err == nil && !ValidateServerName(homeserver) { + err = fmt.Errorf("%q %q", homeserver, ErrNoncompliantServerPart) + } + return +} + +func (userID UserID) ParseAndDecode() (localpart, homeserver string, err error) { + localpart, homeserver, err = userID.ParseAndValidateStrict() + if err == nil { + localpart, err = DecodeUserLocalpart(localpart) + } + return +} + +func (userID UserID) String() string { + return string(userID) +} + +const lowerhex = "0123456789abcdef" + +// encode the given byte using quoted-printable encoding (e.g "=2f") +// and writes it to the buffer +// See https://golang.org/src/mime/quotedprintable/writer.go +func encode(buf *bytes.Buffer, b byte) { + buf.WriteByte('=') + buf.WriteByte(lowerhex[b>>4]) + buf.WriteByte(lowerhex[b&0x0f]) +} + +// escape the given alpha character and writes it to the buffer +func escape(buf *bytes.Buffer, b byte) { + buf.WriteByte('_') + if b == '_' { + buf.WriteByte('_') // another _ + } else { + buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z + } +} + +func shouldEncode(b byte) bool { + return b != '-' && b != '.' && b != '_' && b != '+' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z') +} + +func shouldEscape(b byte) bool { + return (b >= 'A' && b <= 'Z') || b == '_' +} + +func isValidByte(b byte) bool { + return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-' || b == '+' +} + +func isValidEscapedChar(b byte) bool { + return b == '_' || (b >= 'a' && b <= 'z') +} + +// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form. +// See https://spec.matrix.org/v1.2/appendices/#mapping-from-other-character-sets +// +// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z +// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges +// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs) +// and converted to lower-case hex with a leading "=". For example: +// +// Alph@Bet_50up => _alph=40_bet=5f50up +func EncodeUserLocalpart(str string) string { + strBytes := []byte(str) + var outputBuffer bytes.Buffer + for _, b := range strBytes { + if shouldEncode(b) { + encode(&outputBuffer, b) + } else if shouldEscape(b) { + escape(&outputBuffer, b) + } else { + outputBuffer.WriteByte(b) + } + } + return outputBuffer.String() +} + +// DecodeUserLocalpart decodes the given string back into the original input string. +// Returns an error if the given string is not a valid user ID localpart encoding. +// See https://spec.matrix.org/v1.2/appendices/#mapping-from-other-character-sets +// +// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For +// example: +// +// _alph=40_bet=5f50up => Alph@Bet_50up +// +// Returns an error if the input string contains characters outside the +// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has +// an invalid _ escaped byte (e.g. "_5"). +func DecodeUserLocalpart(str string) (string, error) { + strBytes := []byte(str) + var outputBuffer bytes.Buffer + for i := 0; i < len(strBytes); i++ { + b := strBytes[i] + if !isValidByte(b) { + return "", fmt.Errorf("Byte pos %d: Invalid byte", i) + } + + if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _ + if i+1 >= len(strBytes) { + return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i) + } + if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping + return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i) + } + if strBytes[i+1] == '_' { + outputBuffer.WriteByte('_') + } else { + outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z + } + i++ // skip next byte since we just handled it + } else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8 + if i+2 >= len(strBytes) { + return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i) + } + dst := make([]byte, 1) + _, err := hex.Decode(dst, strBytes[i+1:i+3]) + if err != nil { + return "", err + } + outputBuffer.WriteByte(dst[0]) + i += 2 // skip next 2 bytes since we just handled it + } else { // pass through + outputBuffer.WriteByte(b) + } + } + return outputBuffer.String(), nil +} diff --git a/vendor/maunium.net/go/mautrix/pushrules/action.go b/vendor/maunium.net/go/mautrix/pushrules/action.go new file mode 100644 index 00000000..9838e88b --- /dev/null +++ b/vendor/maunium.net/go/mautrix/pushrules/action.go @@ -0,0 +1,125 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pushrules + +import "encoding/json" + +// PushActionType is the type of a PushAction +type PushActionType string + +// The allowed push action types as specified in spec section 11.12.1.4.1. +const ( + ActionNotify PushActionType = "notify" + ActionDontNotify PushActionType = "dont_notify" + ActionCoalesce PushActionType = "coalesce" + ActionSetTweak PushActionType = "set_tweak" +) + +// PushActionTweak is the type of the tweak in SetTweak push actions. +type PushActionTweak string + +// The allowed tweak types as specified in spec section 11.12.1.4.1.1. +const ( + TweakSound PushActionTweak = "sound" + TweakHighlight PushActionTweak = "highlight" +) + +// PushActionArray is an array of PushActions. +type PushActionArray []*PushAction + +// PushActionArrayShould contains the important information parsed from a PushActionArray. +type PushActionArrayShould struct { + // Whether the array contained a Notify, DontNotify or Coalesce action type. + // Deprecated: an empty array should be treated as no notification, so there's no reason to check this field. + NotifySpecified bool + // Whether the event in question should trigger a notification. + Notify bool + // Whether the event in question should be highlighted. + Highlight bool + + // Whether the event in question should trigger a sound alert. + PlaySound bool + // The name of the sound to play if PlaySound is true. + SoundName string +} + +// Should parses this push action array and returns the relevant details wrapped in a PushActionArrayShould struct. +func (actions PushActionArray) Should() (should PushActionArrayShould) { + for _, action := range actions { + switch action.Action { + case ActionNotify, ActionCoalesce: + should.Notify = true + should.NotifySpecified = true + case ActionDontNotify: + should.Notify = false + should.NotifySpecified = true + case ActionSetTweak: + switch action.Tweak { + case TweakHighlight: + var ok bool + should.Highlight, ok = action.Value.(bool) + if !ok { + // Highlight value not specified, so assume true since the tweak is set. + should.Highlight = true + } + case TweakSound: + should.SoundName = action.Value.(string) + should.PlaySound = len(should.SoundName) > 0 + } + } + } + return +} + +// PushAction is a single action that should be triggered when receiving a message. +type PushAction struct { + Action PushActionType + Tweak PushActionTweak + Value interface{} +} + +// UnmarshalJSON parses JSON into this PushAction. +// +// - If the JSON is a single string, the value is stored in the Action field. +// - If the JSON is an object with the set_tweak field, Action will be set to +// "set_tweak", Tweak will be set to the value of the set_tweak field and +// and Value will be set to the value of the value field. +// - In any other case, the function does nothing. +func (action *PushAction) UnmarshalJSON(raw []byte) error { + var data interface{} + + err := json.Unmarshal(raw, &data) + if err != nil { + return err + } + + switch val := data.(type) { + case string: + action.Action = PushActionType(val) + case map[string]interface{}: + tweak, ok := val["set_tweak"].(string) + if ok { + action.Action = ActionSetTweak + action.Tweak = PushActionTweak(tweak) + action.Value, _ = val["value"] + } + } + return nil +} + +// MarshalJSON is the reverse of UnmarshalJSON() +func (action *PushAction) MarshalJSON() (raw []byte, err error) { + if action.Action == ActionSetTweak { + data := map[string]interface{}{ + "set_tweak": action.Tweak, + "value": action.Value, + } + return json.Marshal(&data) + } + data := string(action.Action) + return json.Marshal(&data) +} diff --git a/vendor/maunium.net/go/mautrix/pushrules/condition.go b/vendor/maunium.net/go/mautrix/pushrules/condition.go new file mode 100644 index 00000000..caa717de --- /dev/null +++ b/vendor/maunium.net/go/mautrix/pushrules/condition.go @@ -0,0 +1,359 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pushrules + +import ( + "encoding/json" + "fmt" + "regexp" + "strconv" + "strings" + "unicode" + + "github.com/tidwall/gjson" + "go.mau.fi/util/glob" + + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +// Room is an interface with the functions that are needed for processing room-specific push conditions +type Room interface { + GetOwnDisplayname() string + GetMemberCount() int +} + +type PowerLevelfulRoom interface { + Room + GetPowerLevels() *event.PowerLevelsEventContent +} + +// EventfulRoom is an extension of Room to support MSC3664. +type EventfulRoom interface { + Room + GetEvent(id.EventID) *event.Event +} + +// PushCondKind is the type of a push condition. +type PushCondKind string + +// The allowed push condition kinds as specified in https://spec.matrix.org/v1.2/client-server-api/#conditions-1 +const ( + KindEventMatch PushCondKind = "event_match" + KindContainsDisplayName PushCondKind = "contains_display_name" + KindRoomMemberCount PushCondKind = "room_member_count" + KindEventPropertyIs PushCondKind = "event_property_is" + KindEventPropertyContains PushCondKind = "event_property_contains" + KindSenderNotificationPermission PushCondKind = "sender_notification_permission" + + // MSC3664: https://github.com/matrix-org/matrix-spec-proposals/pull/3664 + + KindRelatedEventMatch PushCondKind = "related_event_match" + KindUnstableRelatedEventMatch PushCondKind = "im.nheko.msc3664.related_event_match" +) + +// PushCondition wraps a condition that is required for a specific PushRule to be used. +type PushCondition struct { + // The type of the condition. + Kind PushCondKind `json:"kind"` + // The dot-separated field of the event to match. Only applicable if kind is EventMatch. + Key string `json:"key,omitempty"` + // The glob-style pattern to match the field against. Only applicable if kind is EventMatch. + Pattern string `json:"pattern,omitempty"` + // The exact value to match the field against. Only applicable if kind is EventPropertyIs or EventPropertyContains. + Value any `json:"value,omitempty"` + // The condition that needs to be fulfilled for RoomMemberCount-type conditions. + // A decimal integer optionally prefixed by ==, <, >, >= or <=. Prefix "==" is assumed if no prefix found. + MemberCountCondition string `json:"is,omitempty"` + + // The relation type for related_event_match from MSC3664 + RelType event.RelationType `json:"rel_type,omitempty"` +} + +// MemberCountFilterRegex is the regular expression to parse the MemberCountCondition of PushConditions. +var MemberCountFilterRegex = regexp.MustCompile("^(==|[<>]=?)?([0-9]+)$") + +// Match checks if this condition is fulfilled for the given event in the given room. +func (cond *PushCondition) Match(room Room, evt *event.Event) bool { + switch cond.Kind { + case KindEventMatch, KindEventPropertyIs, KindEventPropertyContains: + return cond.matchValue(evt) + case KindRelatedEventMatch, KindUnstableRelatedEventMatch: + return cond.matchRelatedEvent(room, evt) + case KindContainsDisplayName: + return cond.matchDisplayName(room, evt) + case KindRoomMemberCount: + return cond.matchMemberCount(room) + case KindSenderNotificationPermission: + return cond.matchSenderNotificationPermission(room, evt.Sender, cond.Key) + default: + return false + } +} + +func splitWithEscaping(s string, separator, escape byte) []string { + var token []byte + var tokens []string + for i := 0; i < len(s); i++ { + if s[i] == separator { + tokens = append(tokens, string(token)) + token = token[:0] + } else if s[i] == escape && i+1 < len(s) { + i++ + token = append(token, s[i]) + } else { + token = append(token, s[i]) + } + } + tokens = append(tokens, string(token)) + return tokens +} + +func hackyNestedGet(data map[string]any, path []string) (any, bool) { + val, ok := data[path[0]] + if len(path) == 1 { + // We don't have any more path parts, return the value regardless of whether it exists or not. + return val, ok + } else if ok { + if mapVal, ok := val.(map[string]any); ok { + val, ok = hackyNestedGet(mapVal, path[1:]) + if ok { + return val, true + } + } + } + // If we don't find the key, try to combine the first two parts. + // e.g. if the key is content.m.relates_to.rel_type, we'll first try data["m"], which will fail, + // then combine m and relates_to to get data["m.relates_to"], which should succeed. + path[1] = path[0] + "." + path[1] + return hackyNestedGet(data, path[1:]) +} + +func stringifyForPushCondition(val interface{}) string { + // Implement MSC3862 to allow matching any type of field + // https://github.com/matrix-org/matrix-spec-proposals/pull/3862 + switch typedVal := val.(type) { + case string: + return typedVal + case nil: + return "null" + case float64: + // Floats aren't allowed in Matrix events, but the JSON parser always stores numbers as floats, + // so just handle that and convert to int + return strconv.FormatInt(int64(typedVal), 10) + default: + return fmt.Sprint(val) + } +} + +func (cond *PushCondition) getValue(evt *event.Event) (any, bool) { + key, subkey, _ := strings.Cut(cond.Key, ".") + + switch key { + case "type": + return evt.Type.Type, true + case "sender": + return evt.Sender.String(), true + case "room_id": + return evt.RoomID.String(), true + case "state_key": + if evt.StateKey == nil { + return nil, false + } + return *evt.StateKey, true + case "content": + // Split the match key with escaping to implement https://github.com/matrix-org/matrix-spec-proposals/pull/3873 + splitKey := splitWithEscaping(subkey, '.', '\\') + // Then do a hacky nested get that supports combining parts for the backwards-compat part of MSC3873 + return hackyNestedGet(evt.Content.Raw, splitKey) + default: + return nil, false + } +} + +func numberToInt64(a any) int64 { + switch typed := a.(type) { + case float64: + return int64(typed) + case float32: + return int64(typed) + case int: + return int64(typed) + case int8: + return int64(typed) + case int16: + return int64(typed) + case int32: + return int64(typed) + case int64: + return typed + case uint: + return int64(typed) + case uint8: + return int64(typed) + case uint16: + return int64(typed) + case uint32: + return int64(typed) + case uint64: + return int64(typed) + default: + return 0 + } +} + +func valueEquals(a, b any) bool { + // Convert floats to ints when comparing numbers (the JSON parser generates floats, but Matrix only allows integers) + // Also allow other numeric types in case something generates events manually without json + switch a.(type) { + case float64, float32, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + switch b.(type) { + case float64, float32, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return numberToInt64(a) == numberToInt64(b) + } + } + return a == b +} + +func (cond *PushCondition) matchValue(evt *event.Event) bool { + val, ok := cond.getValue(evt) + if !ok { + return false + } + + switch cond.Kind { + case KindEventMatch, KindRelatedEventMatch, KindUnstableRelatedEventMatch: + pattern := glob.CompileWithImplicitContains(cond.Pattern) + if pattern == nil { + return false + } + return pattern.Match(stringifyForPushCondition(val)) + case KindEventPropertyIs: + return valueEquals(val, cond.Value) + case KindEventPropertyContains: + valArr, ok := val.([]any) + if !ok { + return false + } + for _, item := range valArr { + if valueEquals(item, cond.Value) { + return true + } + } + return false + default: + panic(fmt.Errorf("matchValue called for unknown condition kind %s", cond.Kind)) + } +} + +func (cond *PushCondition) getRelationEventID(relatesTo *event.RelatesTo) id.EventID { + if relatesTo == nil { + return "" + } + switch cond.RelType { + case "": + return relatesTo.EventID + case "m.in_reply_to": + if relatesTo.IsFallingBack || relatesTo.InReplyTo == nil { + return "" + } + return relatesTo.InReplyTo.EventID + default: + if relatesTo.Type != cond.RelType { + return "" + } + return relatesTo.EventID + } +} + +func (cond *PushCondition) matchRelatedEvent(room Room, evt *event.Event) bool { + var relatesTo *event.RelatesTo + if relatable, ok := evt.Content.Parsed.(event.Relatable); ok { + relatesTo = relatable.OptionalGetRelatesTo() + } else { + res := gjson.GetBytes(evt.Content.VeryRaw, `m\.relates_to`) + if res.Exists() && res.IsObject() { + _ = json.Unmarshal([]byte(res.Raw), &relatesTo) + } + } + if evtID := cond.getRelationEventID(relatesTo); evtID == "" { + return false + } else if eventfulRoom, ok := room.(EventfulRoom); !ok { + return false + } else if evt = eventfulRoom.GetEvent(relatesTo.EventID); evt == nil { + return false + } else { + return cond.matchValue(evt) + } +} + +func (cond *PushCondition) matchDisplayName(room Room, evt *event.Event) bool { + displayname := room.GetOwnDisplayname() + if len(displayname) == 0 { + return false + } + + msg, ok := evt.Content.Raw["body"].(string) + if !ok { + return false + } + + isAcceptable := func(r uint8) bool { + return unicode.IsSpace(rune(r)) || unicode.IsPunct(rune(r)) + } + length := len(displayname) + for index := strings.Index(msg, displayname); index != -1; index = strings.Index(msg, displayname) { + if (index <= 0 || isAcceptable(msg[index-1])) && (index+length >= len(msg) || isAcceptable(msg[index+length])) { + return true + } + msg = msg[index+len(displayname):] + } + return false +} + +func (cond *PushCondition) matchMemberCount(room Room) bool { + group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition) + if len(group) != 3 { + return false + } + + operator := group[1] + wantedMemberCount, _ := strconv.Atoi(group[2]) + + memberCount := room.GetMemberCount() + + switch operator { + case "==", "": + return memberCount == wantedMemberCount + case ">": + return memberCount > wantedMemberCount + case ">=": + return memberCount >= wantedMemberCount + case "<": + return memberCount < wantedMemberCount + case "<=": + return memberCount <= wantedMemberCount + default: + // Should be impossible due to regex. + return false + } +} + +func (cond *PushCondition) matchSenderNotificationPermission(room Room, sender id.UserID, key string) bool { + if key != "room" { + return false + } + plRoom, ok := room.(PowerLevelfulRoom) + if !ok { + return false + } + pls := plRoom.GetPowerLevels() + if pls == nil { + return false + } + return pls.GetUserLevel(sender) >= pls.Notifications.Room() +} diff --git a/vendor/maunium.net/go/mautrix/pushrules/doc.go b/vendor/maunium.net/go/mautrix/pushrules/doc.go new file mode 100644 index 00000000..19cd7745 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/pushrules/doc.go @@ -0,0 +1,2 @@ +// Package pushrules contains utilities to parse push notification rules. +package pushrules diff --git a/vendor/maunium.net/go/mautrix/pushrules/pushrules.go b/vendor/maunium.net/go/mautrix/pushrules/pushrules.go new file mode 100644 index 00000000..7944299a --- /dev/null +++ b/vendor/maunium.net/go/mautrix/pushrules/pushrules.go @@ -0,0 +1,37 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pushrules + +import ( + "encoding/gob" + "encoding/json" + "reflect" + + "maunium.net/go/mautrix/event" +) + +// EventContent represents the content of a m.push_rules account data event. +// https://spec.matrix.org/v1.2/client-server-api/#mpush_rules +type EventContent struct { + Ruleset *PushRuleset `json:"global"` +} + +func init() { + event.TypeMap[event.AccountDataPushRules] = reflect.TypeOf(EventContent{}) + gob.Register(&EventContent{}) +} + +// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON. +func EventToPushRules(evt *event.Event) (*PushRuleset, error) { + content := &EventContent{} + err := json.Unmarshal(evt.Content.VeryRaw, content) + if err != nil { + return nil, err + } + + return content.Ruleset, nil +} diff --git a/vendor/maunium.net/go/mautrix/pushrules/rule.go b/vendor/maunium.net/go/mautrix/pushrules/rule.go new file mode 100644 index 00000000..cf659695 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/pushrules/rule.go @@ -0,0 +1,187 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pushrules + +import ( + "encoding/gob" + "regexp" + "strings" + + "go.mau.fi/util/exerrors" + "go.mau.fi/util/glob" + + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +func init() { + gob.Register(PushRuleArray{}) + gob.Register(PushRuleMap{}) +} + +type PushRuleCollection interface { + GetMatchingRule(room Room, evt *event.Event) *PushRule + GetActions(room Room, evt *event.Event) PushActionArray +} + +type PushRuleArray []*PushRule + +func (rules PushRuleArray) SetType(typ PushRuleType) PushRuleArray { + for _, rule := range rules { + rule.Type = typ + } + return rules +} + +func (rules PushRuleArray) GetMatchingRule(room Room, evt *event.Event) *PushRule { + for _, rule := range rules { + if !rule.Match(room, evt) { + continue + } + return rule + } + return nil +} + +func (rules PushRuleArray) GetActions(room Room, evt *event.Event) PushActionArray { + return rules.GetMatchingRule(room, evt).GetActions() +} + +type PushRuleMap struct { + Map map[string]*PushRule + Type PushRuleType +} + +func (rules PushRuleArray) SetTypeAndMap(typ PushRuleType) PushRuleMap { + data := PushRuleMap{ + Map: make(map[string]*PushRule), + Type: typ, + } + for _, rule := range rules { + rule.Type = typ + data.Map[rule.RuleID] = rule + } + return data +} + +func (ruleMap PushRuleMap) GetMatchingRule(room Room, evt *event.Event) *PushRule { + var rule *PushRule + var found bool + switch ruleMap.Type { + case RoomRule: + rule, found = ruleMap.Map[string(evt.RoomID)] + case SenderRule: + rule, found = ruleMap.Map[string(evt.Sender)] + } + if found && rule.Match(room, evt) { + return rule + } + return nil +} + +func (ruleMap PushRuleMap) GetActions(room Room, evt *event.Event) PushActionArray { + return ruleMap.GetMatchingRule(room, evt).GetActions() +} + +func (ruleMap PushRuleMap) Unmap() PushRuleArray { + array := make(PushRuleArray, len(ruleMap.Map)) + index := 0 + for _, rule := range ruleMap.Map { + array[index] = rule + index++ + } + return array +} + +type PushRuleType string + +const ( + OverrideRule PushRuleType = "override" + ContentRule PushRuleType = "content" + RoomRule PushRuleType = "room" + SenderRule PushRuleType = "sender" + UnderrideRule PushRuleType = "underride" +) + +type PushRule struct { + // The type of this rule. + Type PushRuleType `json:"-"` + // The ID of this rule. + // For room-specific rules and user-specific rules, this is the room or user ID (respectively) + // For other types of rules, this doesn't affect anything. + RuleID string `json:"rule_id"` + // The actions this rule should trigger when matched. + Actions PushActionArray `json:"actions"` + // Whether this is a default rule, or has been set explicitly. + Default bool `json:"default"` + // Whether or not this push rule is enabled. + Enabled bool `json:"enabled"` + // The conditions to match in order to trigger this rule. + // Only applicable to generic underride/override rules. + Conditions []*PushCondition `json:"conditions,omitempty"` + // Pattern for content-specific push rules + Pattern string `json:"pattern,omitempty"` +} + +func (rule *PushRule) GetActions() PushActionArray { + if rule == nil { + return nil + } + return rule.Actions +} + +func (rule *PushRule) Match(room Room, evt *event.Event) bool { + if rule == nil || !rule.Enabled { + return false + } + if rule.RuleID == ".m.rule.contains_display_name" || rule.RuleID == ".m.rule.contains_user_name" || rule.RuleID == ".m.rule.roomnotif" { + if _, containsMentions := evt.Content.Raw["m.mentions"]; containsMentions { + // Disable legacy mention push rules when the event contains the new mentions key + return false + } + } + switch rule.Type { + case OverrideRule, UnderrideRule: + return rule.matchConditions(room, evt) + case ContentRule: + return rule.matchPattern(room, evt) + case RoomRule: + return id.RoomID(rule.RuleID) == evt.RoomID + case SenderRule: + return id.UserID(rule.RuleID) == evt.Sender + default: + return false + } +} + +func (rule *PushRule) matchConditions(room Room, evt *event.Event) bool { + for _, cond := range rule.Conditions { + if !cond.Match(room, evt) { + return false + } + } + return true +} + +func (rule *PushRule) matchPattern(room Room, evt *event.Event) bool { + msg, ok := evt.Content.Raw["body"].(string) + if !ok { + return false + } + var buf strings.Builder + // As per https://spec.matrix.org/unstable/client-server-api/#push-rules, content rules are case-insensitive + // and must match whole words, so wrap the converted glob in (?i) and \b. + buf.WriteString(`(?i)\b`) + // strings.Builder will never return errors + exerrors.PanicIfNotNil(glob.ToRegexPattern(rule.Pattern, &buf)) + buf.WriteString(`\b`) + pattern, err := regexp.Compile(buf.String()) + if err != nil { + return false + } + return pattern.MatchString(msg) +} diff --git a/vendor/maunium.net/go/mautrix/pushrules/ruleset.go b/vendor/maunium.net/go/mautrix/pushrules/ruleset.go new file mode 100644 index 00000000..c42d4799 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/pushrules/ruleset.go @@ -0,0 +1,100 @@ +// Copyright (c) 2020 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pushrules + +import ( + "encoding/json" + + "maunium.net/go/mautrix/event" +) + +type PushRuleset struct { + Override PushRuleArray + Content PushRuleArray + Room PushRuleMap + Sender PushRuleMap + Underride PushRuleArray +} + +type rawPushRuleset struct { + Override PushRuleArray `json:"override"` + Content PushRuleArray `json:"content"` + Room PushRuleArray `json:"room"` + Sender PushRuleArray `json:"sender"` + Underride PushRuleArray `json:"underride"` +} + +// UnmarshalJSON parses JSON into this PushRuleset. +// +// For override, sender and underride push rule arrays, the type is added +// to each PushRule and the array is used as-is. +// +// For room and sender push rule arrays, the type is added to each PushRule +// and the array is converted to a map with the rule ID as the key and the +// PushRule as the value. +func (rs *PushRuleset) UnmarshalJSON(raw []byte) (err error) { + data := rawPushRuleset{} + err = json.Unmarshal(raw, &data) + if err != nil { + return + } + + rs.Override = data.Override.SetType(OverrideRule) + rs.Content = data.Content.SetType(ContentRule) + rs.Room = data.Room.SetTypeAndMap(RoomRule) + rs.Sender = data.Sender.SetTypeAndMap(SenderRule) + rs.Underride = data.Underride.SetType(UnderrideRule) + return +} + +// MarshalJSON is the reverse of UnmarshalJSON() +func (rs *PushRuleset) MarshalJSON() ([]byte, error) { + data := rawPushRuleset{ + Override: rs.Override, + Content: rs.Content, + Room: rs.Room.Unmap(), + Sender: rs.Sender.Unmap(), + Underride: rs.Underride, + } + return json.Marshal(&data) +} + +// DefaultPushActions is the value returned if none of the rule +// collections in a Ruleset match the event given to GetActions() +var DefaultPushActions = PushActionArray{&PushAction{Action: ActionDontNotify}} + +func (rs *PushRuleset) GetMatchingRule(room Room, evt *event.Event) (rule *PushRule) { + if rs == nil { + return nil + } + // Add push rule collections to array in priority order + arrays := []PushRuleCollection{rs.Override, rs.Content, rs.Room, rs.Sender, rs.Underride} + // Loop until one of the push rule collections matches the room/event combo. + for _, pra := range arrays { + if pra == nil { + continue + } + if rule = pra.GetMatchingRule(room, evt); rule != nil { + // Match found, return it. + return + } + } + // No match found + return nil +} + +// GetActions matches the given event against all of the push rule +// collections in this push ruleset in the order of priority as +// specified in spec section 11.12.1.4. +func (rs *PushRuleset) GetActions(room Room, evt *event.Event) (match PushActionArray) { + actions := rs.GetMatchingRule(room, evt).GetActions() + if actions == nil { + // No match found, return default actions. + return DefaultPushActions + } + return actions +} diff --git a/vendor/maunium.net/go/mautrix/requests.go b/vendor/maunium.net/go/mautrix/requests.go new file mode 100644 index 00000000..f0287b3c --- /dev/null +++ b/vendor/maunium.net/go/mautrix/requests.go @@ -0,0 +1,619 @@ +package mautrix + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + "maunium.net/go/mautrix/crypto/signatures" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" + "maunium.net/go/mautrix/pushrules" +) + +type AuthType string + +const ( + AuthTypePassword AuthType = "m.login.password" + AuthTypeReCAPTCHA AuthType = "m.login.recaptcha" + AuthTypeOAuth2 AuthType = "m.login.oauth2" + AuthTypeSSO AuthType = "m.login.sso" + AuthTypeEmail AuthType = "m.login.email.identity" + AuthTypeMSISDN AuthType = "m.login.msisdn" + AuthTypeToken AuthType = "m.login.token" + AuthTypeDummy AuthType = "m.login.dummy" + AuthTypeAppservice AuthType = "m.login.application_service" + + AuthTypeSynapseJWT AuthType = "org.matrix.login.jwt" + + AuthTypeDevtureSharedSecret AuthType = "com.devture.shared_secret_auth" +) + +type IdentifierType string + +const ( + IdentifierTypeUser = "m.id.user" + IdentifierTypeThirdParty = "m.id.thirdparty" + IdentifierTypePhone = "m.id.phone" +) + +type Direction rune + +func (d Direction) MarshalJSON() ([]byte, error) { + return json.Marshal(string(d)) +} + +func (d *Direction) UnmarshalJSON(data []byte) error { + var str string + if err := json.Unmarshal(data, &str); err != nil { + return err + } + switch str { + case "f": + *d = DirectionForward + case "b": + *d = DirectionBackward + default: + return fmt.Errorf("invalid direction %q, must be 'f' or 'b'", str) + } + return nil +} + +const ( + DirectionForward Direction = 'f' + DirectionBackward Direction = 'b' +) + +// ReqRegister is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register +type ReqRegister struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + DeviceID id.DeviceID `json:"device_id,omitempty"` + InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"` + InhibitLogin bool `json:"inhibit_login,omitempty"` + RefreshToken bool `json:"refresh_token,omitempty"` + Auth interface{} `json:"auth,omitempty"` + + // Type for registration, only used for appservice user registrations + // https://spec.matrix.org/v1.2/application-service-api/#server-admin-style-permissions + Type AuthType `json:"type,omitempty"` +} + +type BaseAuthData struct { + Type AuthType `json:"type"` + Session string `json:"session,omitempty"` +} + +type UserIdentifier struct { + Type IdentifierType `json:"type"` + + User string `json:"user,omitempty"` + + Medium string `json:"medium,omitempty"` + Address string `json:"address,omitempty"` + + Country string `json:"country,omitempty"` + Phone string `json:"phone,omitempty"` +} + +// ReqLogin is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login +type ReqLogin struct { + Type AuthType `json:"type"` + Identifier UserIdentifier `json:"identifier"` + Password string `json:"password,omitempty"` + Token string `json:"token,omitempty"` + DeviceID id.DeviceID `json:"device_id,omitempty"` + InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"` + RefreshToken bool `json:"refresh_token,omitempty"` + + // Whether or not the returned credentials should be stored in the Client + StoreCredentials bool `json:"-"` + // Whether or not the returned .well-known data should update the homeserver URL in the Client + StoreHomeserverURL bool `json:"-"` +} + +type ReqPutDevice struct { + DisplayName string `json:"display_name,omitempty"` +} + +type ReqUIAuthFallback struct { + Session string `json:"session"` + User string `json:"user"` +} + +type ReqUIAuthLogin struct { + BaseAuthData + User string `json:"user,omitempty"` + Password string `json:"password,omitempty"` + Token string `json:"token,omitempty"` +} + +// ReqCreateRoom is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom +type ReqCreateRoom struct { + Visibility string `json:"visibility,omitempty"` + RoomAliasName string `json:"room_alias_name,omitempty"` + Name string `json:"name,omitempty"` + Topic string `json:"topic,omitempty"` + Invite []id.UserID `json:"invite,omitempty"` + Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"` + CreationContent map[string]interface{} `json:"creation_content,omitempty"` + InitialState []*event.Event `json:"initial_state,omitempty"` + Preset string `json:"preset,omitempty"` + IsDirect bool `json:"is_direct,omitempty"` + RoomVersion id.RoomVersion `json:"room_version,omitempty"` + + PowerLevelOverride *event.PowerLevelsEventContent `json:"power_level_content_override,omitempty"` + + MeowRoomID id.RoomID `json:"fi.mau.room_id,omitempty"` + MeowCreateTS int64 `json:"fi.mau.origin_server_ts,omitempty"` + BeeperInitialMembers []id.UserID `json:"com.beeper.initial_members,omitempty"` + BeeperAutoJoinInvites bool `json:"com.beeper.auto_join_invites,omitempty"` + BeeperLocalRoomID id.RoomID `json:"com.beeper.local_room_id,omitempty"` + BeeperBridgeName string `json:"com.beeper.bridge_name,omitempty"` + BeeperBridgeAccountID string `json:"com.beeper.bridge_account_id,omitempty"` +} + +// ReqRedact is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidredacteventidtxnid +type ReqRedact struct { + Reason string + TxnID string + Extra map[string]interface{} +} + +type ReqRedactUser struct { + Reason string `json:"reason"` + Limit int `json:"-"` +} + +type ReqMembers struct { + At string `json:"at"` + Membership event.Membership `json:"membership,omitempty"` + NotMembership event.Membership `json:"not_membership,omitempty"` +} + +type ReqJoinRoom struct { + Via []string `json:"-"` + Reason string `json:"reason,omitempty"` + ThirdPartySigned any `json:"third_party_signed,omitempty"` +} + +type ReqKnockRoom struct { + Via []string `json:"-"` + Reason string `json:"reason,omitempty"` +} + +type ReqSearchUserDirectory struct { + SearchTerm string `json:"search_term"` + Limit int `json:"limit,omitempty"` +} + +type ReqMutualRooms struct { + From string `json:"-"` +} + +// ReqInvite3PID is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite-1 +// It is also a JSON object used in https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom +type ReqInvite3PID struct { + IDServer string `json:"id_server"` + Medium string `json:"medium"` + Address string `json:"address"` +} + +type ReqLeave struct { + Reason string `json:"reason,omitempty"` +} + +// ReqInviteUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite +type ReqInviteUser struct { + Reason string `json:"reason,omitempty"` + UserID id.UserID `json:"user_id"` +} + +// ReqKickUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick +type ReqKickUser struct { + Reason string `json:"reason,omitempty"` + UserID id.UserID `json:"user_id"` +} + +// ReqBanUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban +type ReqBanUser struct { + Reason string `json:"reason,omitempty"` + UserID id.UserID `json:"user_id"` + + MSC4293RedactEvents bool `json:"org.matrix.msc4293.redact_events,omitempty"` +} + +// ReqUnbanUser is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban +type ReqUnbanUser struct { + Reason string `json:"reason,omitempty"` + UserID id.UserID `json:"user_id"` +} + +// ReqTyping is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid +type ReqTyping struct { + Typing bool `json:"typing"` + Timeout int64 `json:"timeout,omitempty"` +} + +type ReqPresence struct { + Presence event.Presence `json:"presence"` + StatusMsg string `json:"status_msg,omitempty"` +} + +type ReqAliasCreate struct { + RoomID id.RoomID `json:"room_id"` +} + +type OneTimeKey struct { + Key id.Curve25519 `json:"key"` + Fallback bool `json:"fallback,omitempty"` + Signatures signatures.Signatures `json:"signatures,omitempty"` + Unsigned map[string]any `json:"unsigned,omitempty"` + IsSigned bool `json:"-"` + + // Raw data in the one-time key. This must be used for signature verification to ensure unrecognized fields + // aren't thrown away (because that would invalidate the signature). + RawData json.RawMessage `json:"-"` +} + +type serializableOTK OneTimeKey + +func (otk *OneTimeKey) UnmarshalJSON(data []byte) (err error) { + if len(data) > 0 && data[0] == '"' && data[len(data)-1] == '"' { + err = json.Unmarshal(data, &otk.Key) + otk.Signatures = nil + otk.Unsigned = nil + otk.IsSigned = false + } else { + err = json.Unmarshal(data, (*serializableOTK)(otk)) + otk.RawData = data + otk.IsSigned = true + } + return err +} + +func (otk *OneTimeKey) MarshalJSON() ([]byte, error) { + if !otk.IsSigned { + return json.Marshal(otk.Key) + } else { + return json.Marshal((*serializableOTK)(otk)) + } +} + +type ReqUploadKeys struct { + DeviceKeys *DeviceKeys `json:"device_keys,omitempty"` + OneTimeKeys map[id.KeyID]OneTimeKey `json:"one_time_keys,omitempty"` +} + +type ReqKeysSignatures struct { + UserID id.UserID `json:"user_id"` + DeviceID id.DeviceID `json:"device_id,omitempty"` + Algorithms []id.Algorithm `json:"algorithms,omitempty"` + Usage []id.CrossSigningUsage `json:"usage,omitempty"` + Keys map[id.KeyID]string `json:"keys"` + Signatures signatures.Signatures `json:"signatures"` +} + +type ReqUploadSignatures map[id.UserID]map[string]ReqKeysSignatures + +type DeviceKeys struct { + UserID id.UserID `json:"user_id"` + DeviceID id.DeviceID `json:"device_id"` + Algorithms []id.Algorithm `json:"algorithms"` + Keys KeyMap `json:"keys"` + Signatures signatures.Signatures `json:"signatures"` + Unsigned map[string]interface{} `json:"unsigned,omitempty"` +} + +type CrossSigningKeys struct { + UserID id.UserID `json:"user_id"` + Usage []id.CrossSigningUsage `json:"usage"` + Keys map[id.KeyID]id.Ed25519 `json:"keys"` + Signatures signatures.Signatures `json:"signatures,omitempty"` +} + +func (csk *CrossSigningKeys) FirstKey() id.Ed25519 { + for _, key := range csk.Keys { + return key + } + return "" +} + +type UploadCrossSigningKeysReq struct { + Master CrossSigningKeys `json:"master_key"` + SelfSigning CrossSigningKeys `json:"self_signing_key"` + UserSigning CrossSigningKeys `json:"user_signing_key"` + Auth interface{} `json:"auth,omitempty"` +} + +type KeyMap map[id.DeviceKeyID]string + +func (km KeyMap) GetEd25519(deviceID id.DeviceID) id.Ed25519 { + val, ok := km[id.NewDeviceKeyID(id.KeyAlgorithmEd25519, deviceID)] + if !ok { + return "" + } + return id.Ed25519(val) +} + +func (km KeyMap) GetCurve25519(deviceID id.DeviceID) id.Curve25519 { + val, ok := km[id.NewDeviceKeyID(id.KeyAlgorithmCurve25519, deviceID)] + if !ok { + return "" + } + return id.Curve25519(val) +} + +type ReqQueryKeys struct { + DeviceKeys DeviceKeysRequest `json:"device_keys"` + Timeout int64 `json:"timeout,omitempty"` +} + +type DeviceKeysRequest map[id.UserID]DeviceIDList + +type DeviceIDList []id.DeviceID + +type ReqClaimKeys struct { + OneTimeKeys OneTimeKeysRequest `json:"one_time_keys"` + + Timeout int64 `json:"timeout,omitempty"` +} + +type OneTimeKeysRequest map[id.UserID]map[id.DeviceID]id.KeyAlgorithm + +type ReqSendToDevice struct { + Messages map[id.UserID]map[id.DeviceID]*event.Content `json:"messages"` +} + +type ReqSendEvent struct { + Timestamp int64 + TransactionID string + UnstableDelay time.Duration + + DontEncrypt bool + + MeowEventID id.EventID +} + +type ReqDelayedEvents struct { + DelayID id.DelayID `json:"-"` + Status event.DelayStatus `json:"-"` + NextBatch string `json:"-"` +} + +type ReqUpdateDelayedEvent struct { + DelayID id.DelayID `json:"-"` + Action event.DelayAction `json:"action"` +} + +// ReqDeviceInfo is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3devicesdeviceid +type ReqDeviceInfo struct { + DisplayName string `json:"display_name,omitempty"` +} + +// ReqDeleteDevice is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3devicesdeviceid +type ReqDeleteDevice struct { + Auth interface{} `json:"auth,omitempty"` +} + +// ReqDeleteDevices is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3delete_devices +type ReqDeleteDevices struct { + Devices []id.DeviceID `json:"devices"` + Auth interface{} `json:"auth,omitempty"` +} + +type ReqPutPushRule struct { + Before string `json:"-"` + After string `json:"-"` + + Actions []pushrules.PushActionType `json:"actions"` + Conditions []pushrules.PushCondition `json:"conditions"` + Pattern string `json:"pattern"` +} + +type ReqBeeperBatchSend struct { + // ForwardIfNoMessages should be set to true if the batch should be forward + // backfilled if there are no messages currently in the room. + ForwardIfNoMessages bool `json:"forward_if_no_messages"` + Forward bool `json:"forward"` + SendNotification bool `json:"send_notification"` + MarkReadBy id.UserID `json:"mark_read_by,omitempty"` + Events []*event.Event `json:"events"` +} + +type ReqSetReadMarkers struct { + Read id.EventID `json:"m.read,omitempty"` + ReadPrivate id.EventID `json:"m.read.private,omitempty"` + FullyRead id.EventID `json:"m.fully_read,omitempty"` + + BeeperReadExtra interface{} `json:"com.beeper.read.extra,omitempty"` + BeeperReadPrivateExtra interface{} `json:"com.beeper.read.private.extra,omitempty"` + BeeperFullyReadExtra interface{} `json:"com.beeper.fully_read.extra,omitempty"` +} + +type BeeperInboxDone struct { + Delta int64 `json:"at_delta"` + AtOrder int64 `json:"at_order"` +} + +type ReqSetBeeperInboxState struct { + MarkedUnread *bool `json:"marked_unread,omitempty"` + Done *BeeperInboxDone `json:"done,omitempty"` + ReadMarkers *ReqSetReadMarkers `json:"read_markers,omitempty"` +} + +type ReqSendReceipt struct { + ThreadID string `json:"thread_id,omitempty"` +} + +type ReqPublicRooms struct { + IncludeAllNetworks bool + Limit int + Since string + ThirdPartyInstanceID string +} + +func (req *ReqPublicRooms) Query() map[string]string { + query := map[string]string{} + if req == nil { + return query + } + if req.IncludeAllNetworks { + query["include_all_networks"] = "true" + } + if req.Limit > 0 { + query["limit"] = strconv.Itoa(req.Limit) + } + if req.Since != "" { + query["since"] = req.Since + } + if req.ThirdPartyInstanceID != "" { + query["third_party_instance_id"] = req.ThirdPartyInstanceID + } + return query +} + +// ReqHierarchy contains the parameters for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy +// +// As it's a GET method, there is no JSON body, so this is only query parameters. +type ReqHierarchy struct { + // A pagination token from a previous Hierarchy call. + // If specified, max_depth and suggested_only cannot be changed from the first request. + From string + // Limit for the maximum number of rooms to include per response. + // The server will apply a default value if a limit isn't provided. + Limit int + // Limit for how far to go into the space. When reached, no further child rooms will be returned. + // The server will apply a default value if a max depth isn't provided. + MaxDepth *int + // Flag to indicate whether the server should only consider suggested rooms. + // Suggested rooms are annotated in their m.space.child event contents. + SuggestedOnly bool +} + +func (req *ReqHierarchy) Query() map[string]string { + query := map[string]string{} + if req == nil { + return query + } + if req.From != "" { + query["from"] = req.From + } + if req.Limit > 0 { + query["limit"] = strconv.Itoa(req.Limit) + } + if req.MaxDepth != nil { + query["max_depth"] = strconv.Itoa(*req.MaxDepth) + } + if req.SuggestedOnly { + query["suggested_only"] = "true" + } + return query +} + +type ReqAppservicePing struct { + TxnID string `json:"transaction_id,omitempty"` +} + +type ReqBeeperMergeRoom struct { + NewRoom ReqCreateRoom `json:"create"` + Key string `json:"key"` + Rooms []id.RoomID `json:"rooms"` + User id.UserID `json:"user_id"` +} + +type BeeperSplitRoomPart struct { + UserID id.UserID `json:"user_id"` + Values []string `json:"values"` + NewRoom ReqCreateRoom `json:"create"` +} + +type ReqBeeperSplitRoom struct { + RoomID id.RoomID `json:"-"` + + Key string `json:"key"` + Parts []BeeperSplitRoomPart `json:"parts"` +} + +type ReqRoomKeysVersionCreate[A any] struct { + Algorithm id.KeyBackupAlgorithm `json:"algorithm"` + AuthData A `json:"auth_data"` +} + +type ReqRoomKeysVersionUpdate[A any] struct { + Algorithm id.KeyBackupAlgorithm `json:"algorithm"` + AuthData A `json:"auth_data"` + Version id.KeyBackupVersion `json:"version,omitempty"` +} + +type ReqKeyBackup struct { + Rooms map[id.RoomID]ReqRoomKeyBackup `json:"rooms"` +} + +type ReqRoomKeyBackup struct { + Sessions map[id.SessionID]ReqKeyBackupData `json:"sessions"` +} + +type ReqKeyBackupData struct { + FirstMessageIndex int `json:"first_message_index"` + ForwardedCount int `json:"forwarded_count"` + IsVerified bool `json:"is_verified"` + SessionData json.RawMessage `json:"session_data"` +} + +type ReqReport struct { + Reason string `json:"reason,omitempty"` + Score int `json:"score,omitempty"` +} + +type ReqGetRelations struct { + RelationType event.RelationType + EventType event.Type + + Dir Direction + From string + To string + Limit int + Recurse bool +} + +func (rgr *ReqGetRelations) PathSuffix() ClientURLPath { + if rgr.RelationType != "" { + if rgr.EventType.Type != "" { + return ClientURLPath{rgr.RelationType, rgr.EventType.Type} + } + return ClientURLPath{rgr.RelationType} + } + return ClientURLPath{} +} + +func (rgr *ReqGetRelations) Query() map[string]string { + query := map[string]string{} + if rgr.Dir != 0 { + query["dir"] = string(rgr.Dir) + } + if rgr.From != "" { + query["from"] = rgr.From + } + if rgr.To != "" { + query["to"] = rgr.To + } + if rgr.Limit > 0 { + query["limit"] = strconv.Itoa(rgr.Limit) + } + if rgr.Recurse { + query["recurse"] = "true" + } + return query +} + +// ReqSuspend is the request body for https://github.com/matrix-org/matrix-spec-proposals/pull/4323 +type ReqSuspend struct { + Suspended bool `json:"suspended"` +} + +// ReqLocked is the request body for https://github.com/matrix-org/matrix-spec-proposals/pull/4323 +type ReqLocked struct { + Locked bool `json:"locked"` +} diff --git a/vendor/maunium.net/go/mautrix/responses.go b/vendor/maunium.net/go/mautrix/responses.go new file mode 100644 index 00000000..3484c134 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/responses.go @@ -0,0 +1,757 @@ +package mautrix + +import ( + "bytes" + "encoding/json" + "fmt" + "maps" + "reflect" + "strconv" + "strings" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + "go.mau.fi/util/jsontime" + + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +// RespWhoami is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3accountwhoami +type RespWhoami struct { + UserID id.UserID `json:"user_id"` + DeviceID id.DeviceID `json:"device_id"` +} + +// RespCreateFilter is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridfilter +type RespCreateFilter struct { + FilterID string `json:"filter_id"` +} + +// RespJoinRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidjoin +type RespJoinRoom struct { + RoomID id.RoomID `json:"room_id"` +} + +// RespKnockRoom is the JSON response for https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv3knockroomidoralias +type RespKnockRoom struct { + RoomID id.RoomID `json:"room_id"` +} + +// RespLeaveRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidleave +type RespLeaveRoom struct{} + +// RespForgetRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidforget +type RespForgetRoom struct{} + +// RespInviteUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite +type RespInviteUser struct{} + +// RespKickUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick +type RespKickUser struct{} + +// RespBanUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban +type RespBanUser struct{} + +// RespUnbanUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban +type RespUnbanUser struct{} + +// RespTyping is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid +type RespTyping struct{} + +// RespPresence is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3presenceuseridstatus +type RespPresence struct { + Presence event.Presence `json:"presence"` + LastActiveAgo int `json:"last_active_ago"` + StatusMsg string `json:"status_msg"` + CurrentlyActive bool `json:"currently_active"` +} + +// RespJoinedRooms is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3joined_rooms +type RespJoinedRooms struct { + JoinedRooms []id.RoomID `json:"joined_rooms"` +} + +// RespJoinedMembers is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidjoined_members +type RespJoinedMembers struct { + Joined map[id.UserID]JoinedMember `json:"joined"` +} + +type JoinedMember struct { + DisplayName string `json:"display_name,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` +} + +// RespMessages is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmessages +type RespMessages struct { + Start string `json:"start"` + Chunk []*event.Event `json:"chunk"` + State []*event.Event `json:"state"` + End string `json:"end,omitempty"` +} + +// RespContext is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidcontexteventid +type RespContext struct { + End string `json:"end"` + Event *event.Event `json:"event"` + EventsAfter []*event.Event `json:"events_after"` + EventsBefore []*event.Event `json:"events_before"` + Start string `json:"start"` + State []*event.Event `json:"state"` +} + +// RespSendEvent is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid +type RespSendEvent struct { + EventID id.EventID `json:"event_id"` + + UnstableDelayID id.DelayID `json:"delay_id,omitempty"` +} + +type RespUpdateDelayedEvent struct{} + +type RespDelayedEvents struct { + Scheduled []*event.ScheduledDelayedEvent `json:"scheduled,omitempty"` + Finalised []*event.FinalisedDelayedEvent `json:"finalised,omitempty"` + NextBatch string `json:"next_batch,omitempty"` + + // Deprecated: Synapse implementation still returns this + DelayedEvents []*event.ScheduledDelayedEvent `json:"delayed_events,omitempty"` + // Deprecated: Synapse implementation still returns this + FinalisedEvents []*event.FinalisedDelayedEvent `json:"finalised_events,omitempty"` +} + +type RespRedactUserEvents struct { + IsMoreEvents bool `json:"is_more_events"` + RedactedEvents struct { + Total int `json:"total"` + SoftFailed int `json:"soft_failed"` + } `json:"redacted_events"` +} + +// RespMediaConfig is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixmediav3config +type RespMediaConfig struct { + UploadSize int64 `json:"m.upload.size,omitempty"` +} + +// RespMediaUpload is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixmediav3upload +type RespMediaUpload struct { + ContentURI id.ContentURI `json:"content_uri"` +} + +// RespCreateMXC is the JSON response for https://spec.matrix.org/v1.7/client-server-api/#post_matrixmediav1create +type RespCreateMXC struct { + ContentURI id.ContentURI `json:"content_uri"` + UnusedExpiresAt jsontime.UnixMilli `json:"unused_expires_at,omitempty"` + + UnstableUploadURL string `json:"com.beeper.msc3870.upload_url,omitempty"` + + // Beeper extensions for uploading unique media only once + BeeperUniqueID string `json:"com.beeper.unique_id,omitempty"` + BeeperCompletedAt jsontime.UnixMilli `json:"com.beeper.completed_at,omitempty"` +} + +// RespPreviewURL is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3preview_url +type RespPreviewURL = event.LinkPreview + +// RespUserInteractive is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api +type RespUserInteractive struct { + Flows []UIAFlow `json:"flows,omitempty"` + Params map[AuthType]interface{} `json:"params,omitempty"` + Session string `json:"session,omitempty"` + Completed []string `json:"completed,omitempty"` + + ErrCode string `json:"errcode,omitempty"` + Error string `json:"error,omitempty"` +} + +type UIAFlow struct { + Stages []AuthType `json:"stages,omitempty"` +} + +// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName. +func (r RespUserInteractive) HasSingleStageFlow(stageName AuthType) bool { + for _, f := range r.Flows { + if len(f.Stages) == 1 && f.Stages[0] == stageName { + return true + } + } + return false +} + +// RespUserDisplayName is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseriddisplayname +type RespUserDisplayName struct { + DisplayName string `json:"displayname"` +} + +type RespUserProfile struct { + DisplayName string `json:"displayname,omitempty"` + AvatarURL id.ContentURI `json:"avatar_url,omitempty"` + Extra map[string]any `json:"-"` +} + +type marshalableUserProfile RespUserProfile + +func (r *RespUserProfile) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &r.Extra) + if err != nil { + return err + } + r.DisplayName, _ = r.Extra["displayname"].(string) + avatarURL, _ := r.Extra["avatar_url"].(string) + if avatarURL != "" { + r.AvatarURL, _ = id.ParseContentURI(avatarURL) + } + delete(r.Extra, "displayname") + delete(r.Extra, "avatar_url") + return nil +} + +func (r *RespUserProfile) MarshalJSON() ([]byte, error) { + if len(r.Extra) == 0 { + return json.Marshal((*marshalableUserProfile)(r)) + } + marshalMap := maps.Clone(r.Extra) + if r.DisplayName != "" { + marshalMap["displayname"] = r.DisplayName + } else { + delete(marshalMap, "displayname") + } + if !r.AvatarURL.IsEmpty() { + marshalMap["avatar_url"] = r.AvatarURL.String() + } else { + delete(marshalMap, "avatar_url") + } + return json.Marshal(marshalMap) +} + +type RespSearchUserDirectory struct { + Limited bool `json:"limited"` + Results []*UserDirectoryEntry `json:"results"` +} + +type UserDirectoryEntry struct { + RespUserProfile + UserID id.UserID `json:"user_id"` +} + +func (r *UserDirectoryEntry) UnmarshalJSON(data []byte) error { + err := r.RespUserProfile.UnmarshalJSON(data) + if err != nil { + return err + } + userIDStr, _ := r.Extra["user_id"].(string) + r.UserID = id.UserID(userIDStr) + delete(r.Extra, "user_id") + return nil +} + +func (r *UserDirectoryEntry) MarshalJSON() ([]byte, error) { + if r.Extra == nil { + r.Extra = make(map[string]any) + } + r.Extra["user_id"] = r.UserID.String() + return r.RespUserProfile.MarshalJSON() +} + +type RespMutualRooms struct { + Joined []id.RoomID `json:"joined"` + NextBatch string `json:"next_batch,omitempty"` +} + +type RespRoomSummary struct { + PublicRoomInfo + + Membership event.Membership `json:"membership,omitempty"` + RoomVersion id.RoomVersion `json:"room_version,omitempty"` + Encryption id.Algorithm `json:"encryption,omitempty"` + AllowedRoomIDs []id.RoomID `json:"allowed_room_ids,omitempty"` + + UnstableRoomVersion id.RoomVersion `json:"im.nheko.summary.room_version,omitempty"` + UnstableRoomVersionOld id.RoomVersion `json:"im.nheko.summary.version,omitempty"` + UnstableEncryption id.Algorithm `json:"im.nheko.summary.encryption,omitempty"` +} + +// RespRegisterAvailable is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3registeravailable +type RespRegisterAvailable struct { + Available bool `json:"available"` +} + +// RespRegister is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register +type RespRegister struct { + AccessToken string `json:"access_token,omitempty"` + DeviceID id.DeviceID `json:"device_id,omitempty"` + UserID id.UserID `json:"user_id"` + + RefreshToken string `json:"refresh_token,omitempty"` + ExpiresInMS int64 `json:"expires_in_ms,omitempty"` + + // Deprecated: homeserver should be parsed from the user ID + HomeServer string `json:"home_server,omitempty"` +} + +type LoginFlow struct { + Type AuthType `json:"type"` +} + +// RespLoginFlows is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3login +type RespLoginFlows struct { + Flows []LoginFlow `json:"flows"` +} + +func (rlf *RespLoginFlows) FirstFlowOfType(flowTypes ...AuthType) *LoginFlow { + for _, flow := range rlf.Flows { + for _, flowType := range flowTypes { + if flow.Type == flowType { + return &flow + } + } + } + return nil +} + +func (rlf *RespLoginFlows) HasFlow(flowType ...AuthType) bool { + return rlf.FirstFlowOfType(flowType...) != nil +} + +// RespLogin is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login +type RespLogin struct { + AccessToken string `json:"access_token"` + DeviceID id.DeviceID `json:"device_id"` + UserID id.UserID `json:"user_id"` + WellKnown *ClientWellKnown `json:"well_known,omitempty"` + + RefreshToken string `json:"refresh_token,omitempty"` + ExpiresInMS int64 `json:"expires_in_ms,omitempty"` +} + +// RespLogout is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout +type RespLogout struct{} + +// RespCreateRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom +type RespCreateRoom struct { + RoomID id.RoomID `json:"room_id"` +} + +type RespMembers struct { + Chunk []*event.Event `json:"chunk"` +} + +type LazyLoadSummary struct { + Heroes []id.UserID `json:"m.heroes,omitempty"` + JoinedMemberCount *int `json:"m.joined_member_count,omitempty"` + InvitedMemberCount *int `json:"m.invited_member_count,omitempty"` +} + +type SyncEventsList struct { + Events []*event.Event `json:"events,omitempty"` +} + +type SyncTimeline struct { + SyncEventsList + Limited bool `json:"limited,omitempty"` + PrevBatch string `json:"prev_batch,omitempty"` +} + +// RespSync is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3sync +type RespSync struct { + NextBatch string `json:"next_batch"` + + AccountData SyncEventsList `json:"account_data"` + Presence SyncEventsList `json:"presence"` + ToDevice SyncEventsList `json:"to_device"` + + DeviceLists DeviceLists `json:"device_lists"` + DeviceOTKCount OTKCount `json:"device_one_time_keys_count"` + FallbackKeys []id.KeyAlgorithm `json:"device_unused_fallback_key_types"` + + Rooms RespSyncRooms `json:"rooms"` +} + +type RespSyncRooms struct { + Leave map[id.RoomID]*SyncLeftRoom `json:"leave,omitempty"` + Join map[id.RoomID]*SyncJoinedRoom `json:"join,omitempty"` + Invite map[id.RoomID]*SyncInvitedRoom `json:"invite,omitempty"` + Knock map[id.RoomID]*SyncKnockedRoom `json:"knock,omitempty"` +} + +type marshalableRespSync RespSync + +var syncPathsToDelete = []string{"account_data", "presence", "to_device", "device_lists", "device_one_time_keys_count", "rooms"} + +// marshalAndDeleteEmpty marshals a JSON object, then uses gjson to delete empty objects at the given gjson paths. +func marshalAndDeleteEmpty(marshalable interface{}, paths []string) ([]byte, error) { + data, err := json.Marshal(marshalable) + if err != nil { + return nil, err + } + for _, path := range paths { + res := gjson.GetBytes(data, path) + if res.IsObject() && len(res.Raw) == 2 { + data, err = sjson.DeleteBytes(data, path) + if err != nil { + return nil, fmt.Errorf("failed to delete empty %s: %w", path, err) + } + } + } + return data, nil +} + +func (rs *RespSync) MarshalJSON() ([]byte, error) { + return marshalAndDeleteEmpty((*marshalableRespSync)(rs), syncPathsToDelete) +} + +type DeviceLists struct { + Changed []id.UserID `json:"changed,omitempty"` + Left []id.UserID `json:"left,omitempty"` +} + +type OTKCount struct { + Curve25519 int `json:"curve25519,omitempty"` + SignedCurve25519 int `json:"signed_curve25519,omitempty"` + + // For appservice OTK counts only: the user ID in question + UserID id.UserID `json:"-"` + DeviceID id.DeviceID `json:"-"` +} + +type SyncLeftRoom struct { + Summary LazyLoadSummary `json:"summary"` + State SyncEventsList `json:"state"` + Timeline SyncTimeline `json:"timeline"` +} + +type marshalableSyncLeftRoom SyncLeftRoom + +var syncLeftRoomPathsToDelete = []string{"summary", "state", "timeline"} + +func (slr SyncLeftRoom) MarshalJSON() ([]byte, error) { + return marshalAndDeleteEmpty((marshalableSyncLeftRoom)(slr), syncLeftRoomPathsToDelete) +} + +type BeeperInboxPreviewEvent struct { + EventID id.EventID `json:"event_id"` + Timestamp jsontime.UnixMilli `json:"origin_server_ts"` + Event *event.Event `json:"event,omitempty"` +} + +type SyncJoinedRoom struct { + Summary LazyLoadSummary `json:"summary"` + State SyncEventsList `json:"state"` + StateAfter *SyncEventsList `json:"state_after,omitempty"` + Timeline SyncTimeline `json:"timeline"` + Ephemeral SyncEventsList `json:"ephemeral"` + AccountData SyncEventsList `json:"account_data"` + + UnreadNotifications *UnreadNotificationCounts `json:"unread_notifications,omitempty"` + // https://github.com/matrix-org/matrix-spec-proposals/pull/2654 + MSC2654UnreadCount *int `json:"org.matrix.msc2654.unread_count,omitempty"` + // Beeper extension + BeeperInboxPreview *BeeperInboxPreviewEvent `json:"com.beeper.inbox.preview,omitempty"` +} + +type UnreadNotificationCounts struct { + HighlightCount int `json:"highlight_count"` + NotificationCount int `json:"notification_count"` +} + +type marshalableSyncJoinedRoom SyncJoinedRoom + +var syncJoinedRoomPathsToDelete = []string{"summary", "state", "timeline", "ephemeral", "account_data"} + +func (sjr SyncJoinedRoom) MarshalJSON() ([]byte, error) { + return marshalAndDeleteEmpty((marshalableSyncJoinedRoom)(sjr), syncJoinedRoomPathsToDelete) +} + +type SyncInvitedRoom struct { + State SyncEventsList `json:"invite_state"` +} + +type SyncKnockedRoom struct { + State SyncEventsList `json:"knock_state"` +} + +type RespTurnServer struct { + Username string `json:"username"` + Password string `json:"password"` + TTL int `json:"ttl"` + URIs []string `json:"uris"` +} + +type RespAliasCreate struct{} +type RespAliasDelete struct{} +type RespAliasResolve struct { + RoomID id.RoomID `json:"room_id"` + Servers []string `json:"servers"` +} +type RespAliasList struct { + Aliases []id.RoomAlias `json:"aliases"` +} + +type RespUploadKeys struct { + OneTimeKeyCounts OTKCount `json:"one_time_key_counts"` +} + +type RespQueryKeys struct { + Failures map[string]interface{} `json:"failures,omitempty"` + DeviceKeys map[id.UserID]map[id.DeviceID]DeviceKeys `json:"device_keys"` + MasterKeys map[id.UserID]CrossSigningKeys `json:"master_keys"` + SelfSigningKeys map[id.UserID]CrossSigningKeys `json:"self_signing_keys"` + UserSigningKeys map[id.UserID]CrossSigningKeys `json:"user_signing_keys"` +} + +type RespClaimKeys struct { + Failures map[string]interface{} `json:"failures,omitempty"` + OneTimeKeys map[id.UserID]map[id.DeviceID]map[id.KeyID]OneTimeKey `json:"one_time_keys"` +} + +type RespUploadSignatures struct { + Failures map[string]interface{} `json:"failures,omitempty"` +} + +type RespKeyChanges struct { + Changed []id.UserID `json:"changed"` + Left []id.UserID `json:"left"` +} + +type RespSendToDevice struct{} + +// RespDevicesInfo is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devices +type RespDevicesInfo struct { + Devices []RespDeviceInfo `json:"devices"` +} + +// RespDeviceInfo is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devicesdeviceid +type RespDeviceInfo struct { + DeviceID id.DeviceID `json:"device_id"` + DisplayName string `json:"display_name"` + LastSeenIP string `json:"last_seen_ip"` + LastSeenTS int64 `json:"last_seen_ts"` +} + +type RespBeeperBatchSend struct { + EventIDs []id.EventID `json:"event_ids"` +} + +// RespCapabilities is the JSON response for https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3capabilities +type RespCapabilities struct { + RoomVersions *CapRoomVersions `json:"m.room_versions,omitempty"` + ChangePassword *CapBooleanTrue `json:"m.change_password,omitempty"` + SetDisplayname *CapBooleanTrue `json:"m.set_displayname,omitempty"` + SetAvatarURL *CapBooleanTrue `json:"m.set_avatar_url,omitempty"` + ThreePIDChanges *CapBooleanTrue `json:"m.3pid_changes,omitempty"` + GetLoginToken *CapBooleanTrue `json:"m.get_login_token,omitempty"` + UnstableAccountModeration *CapUnstableAccountModeration `json:"uk.timedout.msc4323,omitempty"` + + Custom map[string]interface{} `json:"-"` +} + +type serializableRespCapabilities RespCapabilities + +func (rc *RespCapabilities) UnmarshalJSON(data []byte) error { + res := gjson.GetBytes(data, "capabilities") + if !res.Exists() || !res.IsObject() { + return nil + } + if res.Index > 0 { + data = data[res.Index : res.Index+len(res.Raw)] + } else { + data = []byte(res.Raw) + } + err := json.Unmarshal(data, (*serializableRespCapabilities)(rc)) + if err != nil { + return err + } + err = json.Unmarshal(data, &rc.Custom) + if err != nil { + return err + } + // Remove non-custom capabilities from the custom map so that they don't get overridden when serializing back + for _, field := range reflect.VisibleFields(reflect.TypeOf(rc).Elem()) { + jsonTag := strings.Split(field.Tag.Get("json"), ",")[0] + if jsonTag != "-" && jsonTag != "" { + delete(rc.Custom, jsonTag) + } + } + return nil +} + +func (rc *RespCapabilities) MarshalJSON() ([]byte, error) { + marshalableCopy := make(map[string]interface{}, len(rc.Custom)) + val := reflect.ValueOf(rc).Elem() + for _, field := range reflect.VisibleFields(val.Type()) { + jsonTag := strings.Split(field.Tag.Get("json"), ",")[0] + if jsonTag != "-" && jsonTag != "" { + fieldVal := val.FieldByIndex(field.Index) + if !fieldVal.IsNil() { + marshalableCopy[jsonTag] = fieldVal.Interface() + } + } + } + if rc.Custom != nil { + for key, value := range rc.Custom { + marshalableCopy[key] = value + } + } + var buf bytes.Buffer + buf.WriteString(`{"capabilities":`) + err := json.NewEncoder(&buf).Encode(marshalableCopy) + if err != nil { + return nil, err + } + buf.WriteByte('}') + return buf.Bytes(), nil +} + +type CapBoolean struct { + Enabled bool `json:"enabled"` +} + +type CapBooleanTrue CapBoolean + +// IsEnabled returns true if the capability is either enabled explicitly or not specified (nil) +func (cb *CapBooleanTrue) IsEnabled() bool { + // Default to true when + return cb == nil || cb.Enabled +} + +type CapBooleanFalse CapBoolean + +// IsEnabled returns true if the capability is enabled explicitly. If it's not specified, this returns false. +func (cb *CapBooleanFalse) IsEnabled() bool { + return cb != nil && cb.Enabled +} + +type CapRoomVersionStability string + +const ( + CapRoomVersionStable CapRoomVersionStability = "stable" + CapRoomVersionUnstable CapRoomVersionStability = "unstable" +) + +type CapRoomVersions struct { + Default string `json:"default"` + Available map[string]CapRoomVersionStability `json:"available"` +} + +func (vers *CapRoomVersions) IsStable(version string) bool { + if vers == nil || vers.Available == nil { + val, err := strconv.Atoi(version) + return err == nil && val > 0 + } + return vers.Available[version] == CapRoomVersionStable +} + +func (vers *CapRoomVersions) IsAvailable(version string) bool { + if vers == nil || vers.Available == nil { + return false + } + _, available := vers.Available[version] + return available +} + +type CapUnstableAccountModeration struct { + Suspend bool `json:"suspend"` + Lock bool `json:"lock"` +} + +type RespPublicRooms struct { + Chunk []*PublicRoomInfo `json:"chunk"` + NextBatch string `json:"next_batch,omitempty"` + PrevBatch string `json:"prev_batch,omitempty"` + TotalRoomCountEstimate int `json:"total_room_count_estimate"` +} + +type PublicRoomInfo struct { + RoomID id.RoomID `json:"room_id"` + AvatarURL id.ContentURIString `json:"avatar_url,omitempty"` + CanonicalAlias id.RoomAlias `json:"canonical_alias,omitempty"` + GuestCanJoin bool `json:"guest_can_join"` + JoinRule event.JoinRule `json:"join_rule,omitempty"` + Name string `json:"name,omitempty"` + NumJoinedMembers int `json:"num_joined_members"` + RoomType event.RoomType `json:"room_type"` + Topic string `json:"topic,omitempty"` + WorldReadable bool `json:"world_readable"` +} + +// RespHierarchy is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy +type RespHierarchy struct { + NextBatch string `json:"next_batch,omitempty"` + Rooms []*ChildRoomsChunk `json:"rooms"` +} + +type ChildRoomsChunk struct { + PublicRoomInfo + ChildrenState []*event.Event `json:"children_state"` +} + +type RespAppservicePing struct { + DurationMS int64 `json:"duration_ms"` +} + +type RespBeeperMergeRoom RespCreateRoom + +type RespBeeperSplitRoom struct { + RoomIDs map[string]id.RoomID `json:"room_ids"` +} + +type RespTimestampToEvent struct { + EventID id.EventID `json:"event_id"` + Timestamp jsontime.UnixMilli `json:"origin_server_ts"` +} + +type RespRoomKeysVersionCreate struct { + Version id.KeyBackupVersion `json:"version"` +} + +type RespRoomKeysVersion[A any] struct { + Algorithm id.KeyBackupAlgorithm `json:"algorithm"` + AuthData A `json:"auth_data"` + Count int `json:"count"` + ETag string `json:"etag"` + Version id.KeyBackupVersion `json:"version"` +} + +type RespRoomKeys[S any] struct { + Rooms map[id.RoomID]RespRoomKeyBackup[S] `json:"rooms"` +} + +type RespRoomKeyBackup[S any] struct { + Sessions map[id.SessionID]RespKeyBackupData[S] `json:"sessions"` +} + +type RespKeyBackupData[S any] struct { + FirstMessageIndex int `json:"first_message_index"` + ForwardedCount int `json:"forwarded_count"` + IsVerified bool `json:"is_verified"` + SessionData S `json:"session_data"` +} + +type RespRoomKeysUpdate struct { + Count int `json:"count"` + ETag string `json:"etag"` +} + +type RespOpenIDToken struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + MatrixServerName string `json:"matrix_server_name"` + TokenType string `json:"token_type"` // Always "Bearer" +} + +type RespGetRelations struct { + Chunk []*event.Event `json:"chunk"` + NextBatch string `json:"next_batch,omitempty"` + PrevBatch string `json:"prev_batch,omitempty"` + RecursionDepth int `json:"recursion_depth,omitempty"` +} + +// RespSuspended is the response body for https://github.com/matrix-org/matrix-spec-proposals/pull/4323 +type RespSuspended struct { + Suspended bool `json:"suspended"` +} + +// RespLocked is the response body for https://github.com/matrix-org/matrix-spec-proposals/pull/4323 +type RespLocked struct { + Locked bool `json:"locked"` +} diff --git a/vendor/maunium.net/go/mautrix/room.go b/vendor/maunium.net/go/mautrix/room.go new file mode 100644 index 00000000..c3ddb7e6 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/room.go @@ -0,0 +1,54 @@ +package mautrix + +import ( + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +type RoomStateMap = map[event.Type]map[string]*event.Event + +// Room represents a single Matrix room. +type Room struct { + ID id.RoomID + State RoomStateMap +} + +// UpdateState updates the room's current state with the given Event. This will clobber events based +// on the type/state_key combination. +func (room Room) UpdateState(evt *event.Event) { + _, exists := room.State[evt.Type] + if !exists { + room.State[evt.Type] = make(map[string]*event.Event) + } + room.State[evt.Type][*evt.StateKey] = evt +} + +// GetStateEvent returns the state event for the given type/state_key combo, or nil. +func (room Room) GetStateEvent(eventType event.Type, stateKey string) *event.Event { + stateEventMap, _ := room.State[eventType] + evt, _ := stateEventMap[stateKey] + return evt +} + +// GetMembershipState returns the membership state of the given user ID in this room. If there is +// no entry for this member, 'leave' is returned for consistency with left users. +func (room Room) GetMembershipState(userID id.UserID) event.Membership { + state := event.MembershipLeave + evt := room.GetStateEvent(event.StateMember, string(userID)) + if evt != nil { + membership, ok := evt.Content.Raw["membership"].(string) + if ok { + state = event.Membership(membership) + } + } + return state +} + +// NewRoom creates a new Room with the given ID +func NewRoom(roomID id.RoomID) *Room { + // Init the State map and return a pointer to the Room + return &Room{ + ID: roomID, + State: make(RoomStateMap), + } +} diff --git a/vendor/maunium.net/go/mautrix/statestore.go b/vendor/maunium.net/go/mautrix/statestore.go new file mode 100644 index 00000000..1933ab95 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/statestore.go @@ -0,0 +1,371 @@ +// Copyright (c) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mautrix + +import ( + "context" + "maps" + "sync" + + "github.com/rs/zerolog" + "go.mau.fi/util/exerrors" + + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +// StateStore is an interface for storing basic room state information. +type StateStore interface { + IsInRoom(ctx context.Context, roomID id.RoomID, userID id.UserID) bool + IsInvited(ctx context.Context, roomID id.RoomID, userID id.UserID) bool + IsMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool + GetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, error) + TryGetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, error) + SetMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, membership event.Membership) error + SetMember(ctx context.Context, roomID id.RoomID, userID id.UserID, member *event.MemberEventContent) error + IsConfusableName(ctx context.Context, roomID id.RoomID, currentUser id.UserID, name string) ([]id.UserID, error) + ClearCachedMembers(ctx context.Context, roomID id.RoomID, memberships ...event.Membership) error + ReplaceCachedMembers(ctx context.Context, roomID id.RoomID, evts []*event.Event, onlyMemberships ...event.Membership) error + + SetPowerLevels(ctx context.Context, roomID id.RoomID, levels *event.PowerLevelsEventContent) error + GetPowerLevels(ctx context.Context, roomID id.RoomID) (*event.PowerLevelsEventContent, error) + + SetCreate(ctx context.Context, evt *event.Event) error + GetCreate(ctx context.Context, roomID id.RoomID) (*event.Event, error) + + HasFetchedMembers(ctx context.Context, roomID id.RoomID) (bool, error) + MarkMembersFetched(ctx context.Context, roomID id.RoomID) error + GetAllMembers(ctx context.Context, roomID id.RoomID) (map[id.UserID]*event.MemberEventContent, error) + + SetEncryptionEvent(ctx context.Context, roomID id.RoomID, content *event.EncryptionEventContent) error + IsEncrypted(ctx context.Context, roomID id.RoomID) (bool, error) + + GetRoomJoinedOrInvitedMembers(ctx context.Context, roomID id.RoomID) ([]id.UserID, error) +} + +type StateStoreUpdater interface { + UpdateState(ctx context.Context, evt *event.Event) +} + +func UpdateStateStore(ctx context.Context, store StateStore, evt *event.Event) { + if store == nil || evt == nil || evt.StateKey == nil { + return + } + if directUpdater, ok := store.(StateStoreUpdater); ok { + directUpdater.UpdateState(ctx, evt) + return + } + // We only care about events without a state key (power levels, encryption) or member events with state key + if evt.Type != event.StateMember && evt.GetStateKey() != "" { + return + } + var err error + switch content := evt.Content.Parsed.(type) { + case *event.MemberEventContent: + err = store.SetMember(ctx, evt.RoomID, id.UserID(evt.GetStateKey()), content) + case *event.PowerLevelsEventContent: + err = store.SetPowerLevels(ctx, evt.RoomID, content) + case *event.EncryptionEventContent: + err = store.SetEncryptionEvent(ctx, evt.RoomID, content) + case *event.CreateEventContent: + err = store.SetCreate(ctx, evt) + default: + switch evt.Type { + case event.StateMember, event.StatePowerLevels, event.StateEncryption, event.StateCreate: + zerolog.Ctx(ctx).Warn(). + Stringer("event_id", evt.ID). + Str("event_type", evt.Type.Type). + Type("content_type", evt.Content.Parsed). + Msg("Got known event type with unknown content type in UpdateStateStore") + } + } + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err). + Stringer("event_id", evt.ID). + Str("event_type", evt.Type.Type). + Msg("Failed to update state store") + } +} + +// StateStoreSyncHandler can be added as an event handler in the syncer to update the state store automatically. +// +// client.Syncer.(mautrix.ExtensibleSyncer).OnEvent(client.StateStoreSyncHandler) +// +// DefaultSyncer.ParseEventContent must also be true for this to work (which it is by default). +func (cli *Client) StateStoreSyncHandler(ctx context.Context, evt *event.Event) { + UpdateStateStore(ctx, cli.StateStore, evt) +} + +type MemoryStateStore struct { + Registrations map[id.UserID]bool `json:"registrations"` + Members map[id.RoomID]map[id.UserID]*event.MemberEventContent `json:"memberships"` + MembersFetched map[id.RoomID]bool `json:"members_fetched"` + PowerLevels map[id.RoomID]*event.PowerLevelsEventContent `json:"power_levels"` + Encryption map[id.RoomID]*event.EncryptionEventContent `json:"encryption"` + Create map[id.RoomID]*event.Event `json:"create"` + + registrationsLock sync.RWMutex + membersLock sync.RWMutex + powerLevelsLock sync.RWMutex + encryptionLock sync.RWMutex +} + +func NewMemoryStateStore() StateStore { + return &MemoryStateStore{ + Registrations: make(map[id.UserID]bool), + Members: make(map[id.RoomID]map[id.UserID]*event.MemberEventContent), + MembersFetched: make(map[id.RoomID]bool), + PowerLevels: make(map[id.RoomID]*event.PowerLevelsEventContent), + Encryption: make(map[id.RoomID]*event.EncryptionEventContent), + Create: make(map[id.RoomID]*event.Event), + } +} + +func (store *MemoryStateStore) IsRegistered(_ context.Context, userID id.UserID) (bool, error) { + store.registrationsLock.RLock() + defer store.registrationsLock.RUnlock() + registered, ok := store.Registrations[userID] + return ok && registered, nil +} + +func (store *MemoryStateStore) MarkRegistered(_ context.Context, userID id.UserID) error { + store.registrationsLock.Lock() + defer store.registrationsLock.Unlock() + store.Registrations[userID] = true + return nil +} + +func (store *MemoryStateStore) GetRoomMembers(_ context.Context, roomID id.RoomID) (map[id.UserID]*event.MemberEventContent, error) { + store.membersLock.RLock() + members, ok := store.Members[roomID] + store.membersLock.RUnlock() + if !ok { + members = make(map[id.UserID]*event.MemberEventContent) + store.membersLock.Lock() + store.Members[roomID] = members + store.membersLock.Unlock() + } + return members, nil +} + +func (store *MemoryStateStore) GetRoomJoinedOrInvitedMembers(ctx context.Context, roomID id.RoomID) ([]id.UserID, error) { + members, err := store.GetRoomMembers(ctx, roomID) + if err != nil { + return nil, err + } + ids := make([]id.UserID, 0, len(members)) + for id := range members { + ids = append(ids, id) + } + return ids, nil +} + +func (store *MemoryStateStore) GetMembership(ctx context.Context, roomID id.RoomID, userID id.UserID) (event.Membership, error) { + return exerrors.Must(store.GetMember(ctx, roomID, userID)).Membership, nil +} + +func (store *MemoryStateStore) GetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, error) { + member, err := store.TryGetMember(ctx, roomID, userID) + if member == nil && err == nil { + member = &event.MemberEventContent{Membership: event.MembershipLeave} + } + return member, err +} + +func (store *MemoryStateStore) IsConfusableName(ctx context.Context, roomID id.RoomID, currentUser id.UserID, name string) ([]id.UserID, error) { + // TODO implement? + return nil, nil +} + +func (store *MemoryStateStore) TryGetMember(_ context.Context, roomID id.RoomID, userID id.UserID) (member *event.MemberEventContent, err error) { + store.membersLock.RLock() + defer store.membersLock.RUnlock() + members, membersOk := store.Members[roomID] + if !membersOk { + return + } + member = members[userID] + return +} + +func (store *MemoryStateStore) IsInRoom(ctx context.Context, roomID id.RoomID, userID id.UserID) bool { + return store.IsMembership(ctx, roomID, userID, "join") +} + +func (store *MemoryStateStore) IsInvited(ctx context.Context, roomID id.RoomID, userID id.UserID) bool { + return store.IsMembership(ctx, roomID, userID, "join", "invite") +} + +func (store *MemoryStateStore) IsMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool { + membership := exerrors.Must(store.GetMembership(ctx, roomID, userID)) + for _, allowedMembership := range allowedMemberships { + if allowedMembership == membership { + return true + } + } + return false +} + +func (store *MemoryStateStore) SetMembership(_ context.Context, roomID id.RoomID, userID id.UserID, membership event.Membership) error { + store.membersLock.Lock() + members, ok := store.Members[roomID] + if !ok { + members = map[id.UserID]*event.MemberEventContent{ + userID: {Membership: membership}, + } + } else { + member, ok := members[userID] + if !ok { + members[userID] = &event.MemberEventContent{Membership: membership} + } else { + member.Membership = membership + members[userID] = member + } + } + store.Members[roomID] = members + store.membersLock.Unlock() + return nil +} + +func (store *MemoryStateStore) SetMember(_ context.Context, roomID id.RoomID, userID id.UserID, member *event.MemberEventContent) error { + store.membersLock.Lock() + members, ok := store.Members[roomID] + if !ok { + members = map[id.UserID]*event.MemberEventContent{ + userID: member, + } + } else { + members[userID] = member + } + store.Members[roomID] = members + store.membersLock.Unlock() + return nil +} + +func (store *MemoryStateStore) ClearCachedMembers(_ context.Context, roomID id.RoomID, memberships ...event.Membership) error { + store.membersLock.Lock() + defer store.membersLock.Unlock() + members, ok := store.Members[roomID] + if !ok { + return nil + } + for userID, member := range members { + for _, membership := range memberships { + if membership == member.Membership { + delete(members, userID) + break + } + } + } + store.MembersFetched[roomID] = false + return nil +} + +func (store *MemoryStateStore) HasFetchedMembers(ctx context.Context, roomID id.RoomID) (bool, error) { + store.membersLock.RLock() + defer store.membersLock.RUnlock() + return store.MembersFetched[roomID], nil +} + +func (store *MemoryStateStore) MarkMembersFetched(ctx context.Context, roomID id.RoomID) error { + store.membersLock.Lock() + defer store.membersLock.Unlock() + store.MembersFetched[roomID] = true + return nil +} + +func (store *MemoryStateStore) ReplaceCachedMembers(ctx context.Context, roomID id.RoomID, evts []*event.Event, onlyMemberships ...event.Membership) error { + _ = store.ClearCachedMembers(ctx, roomID, onlyMemberships...) + for _, evt := range evts { + UpdateStateStore(ctx, store, evt) + } + if len(onlyMemberships) == 0 { + _ = store.MarkMembersFetched(ctx, roomID) + } + return nil +} + +func (store *MemoryStateStore) GetAllMembers(ctx context.Context, roomID id.RoomID) (map[id.UserID]*event.MemberEventContent, error) { + store.membersLock.RLock() + defer store.membersLock.RUnlock() + return maps.Clone(store.Members[roomID]), nil +} + +func (store *MemoryStateStore) SetPowerLevels(_ context.Context, roomID id.RoomID, levels *event.PowerLevelsEventContent) error { + store.powerLevelsLock.Lock() + store.PowerLevels[roomID] = levels + store.powerLevelsLock.Unlock() + return nil +} + +func (store *MemoryStateStore) GetPowerLevels(_ context.Context, roomID id.RoomID) (levels *event.PowerLevelsEventContent, err error) { + store.powerLevelsLock.RLock() + levels = store.PowerLevels[roomID] + if levels != nil && levels.CreateEvent == nil { + levels.CreateEvent = store.Create[roomID] + } + store.powerLevelsLock.RUnlock() + return +} + +func (store *MemoryStateStore) GetPowerLevel(ctx context.Context, roomID id.RoomID, userID id.UserID) (int, error) { + return exerrors.Must(store.GetPowerLevels(ctx, roomID)).GetUserLevel(userID), nil +} + +func (store *MemoryStateStore) GetPowerLevelRequirement(ctx context.Context, roomID id.RoomID, eventType event.Type) (int, error) { + return exerrors.Must(store.GetPowerLevels(ctx, roomID)).GetEventLevel(eventType), nil +} + +func (store *MemoryStateStore) HasPowerLevel(ctx context.Context, roomID id.RoomID, userID id.UserID, eventType event.Type) (bool, error) { + return exerrors.Must(store.GetPowerLevel(ctx, roomID, userID)) >= exerrors.Must(store.GetPowerLevelRequirement(ctx, roomID, eventType)), nil +} + +func (store *MemoryStateStore) SetCreate(ctx context.Context, evt *event.Event) error { + store.powerLevelsLock.Lock() + store.Create[evt.RoomID] = evt + if pls, ok := store.PowerLevels[evt.RoomID]; ok && pls.CreateEvent == nil { + pls.CreateEvent = evt + } + store.powerLevelsLock.Unlock() + return nil +} + +func (store *MemoryStateStore) GetCreate(ctx context.Context, roomID id.RoomID) (*event.Event, error) { + store.powerLevelsLock.RLock() + evt := store.Create[roomID] + store.powerLevelsLock.RUnlock() + return evt, nil +} + +func (store *MemoryStateStore) SetEncryptionEvent(_ context.Context, roomID id.RoomID, content *event.EncryptionEventContent) error { + store.encryptionLock.Lock() + store.Encryption[roomID] = content + store.encryptionLock.Unlock() + return nil +} + +func (store *MemoryStateStore) GetEncryptionEvent(_ context.Context, roomID id.RoomID) (*event.EncryptionEventContent, error) { + store.encryptionLock.RLock() + defer store.encryptionLock.RUnlock() + return store.Encryption[roomID], nil +} + +func (store *MemoryStateStore) IsEncrypted(ctx context.Context, roomID id.RoomID) (bool, error) { + cfg, err := store.GetEncryptionEvent(ctx, roomID) + return cfg != nil && cfg.Algorithm == id.AlgorithmMegolmV1, err +} + +func (store *MemoryStateStore) FindSharedRooms(ctx context.Context, userID id.UserID) (rooms []id.RoomID, err error) { + store.membersLock.RLock() + defer store.membersLock.RUnlock() + for roomID, members := range store.Members { + if _, ok := members[userID]; ok { + rooms = append(rooms, roomID) + } + } + return rooms, nil +} diff --git a/vendor/maunium.net/go/mautrix/sync.go b/vendor/maunium.net/go/mautrix/sync.go new file mode 100644 index 00000000..598df8e0 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/sync.go @@ -0,0 +1,286 @@ +// Copyright (c) 2024 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mautrix + +import ( + "context" + "errors" + "fmt" + "runtime/debug" + "time" + + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +// EventHandler handles a single event from a sync response. +type EventHandler func(ctx context.Context, evt *event.Event) + +// SyncHandler handles a whole sync response. If the return value is false, handling will be stopped completely. +type SyncHandler func(ctx context.Context, resp *RespSync, since string) bool + +// Syncer is an interface that must be satisfied in order to do /sync requests on a client. +type Syncer interface { + // ProcessResponse processes the /sync response. The since parameter is the since= value that was used to produce the response. + // This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped permanently. + ProcessResponse(ctx context.Context, resp *RespSync, since string) error + // OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently. + OnFailedSync(res *RespSync, err error) (time.Duration, error) + // GetFilterJSON for the given user ID. NOT the filter ID. + GetFilterJSON(userID id.UserID) *Filter +} + +type ExtensibleSyncer interface { + OnSync(callback SyncHandler) + OnEvent(callback EventHandler) + OnEventType(eventType event.Type, callback EventHandler) +} + +type DispatchableSyncer interface { + Dispatch(ctx context.Context, evt *event.Event) +} + +// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively +// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer +// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information. +type DefaultSyncer struct { + // syncListeners want the whole sync response, e.g. the crypto machine + syncListeners []SyncHandler + // globalListeners want all events + globalListeners []EventHandler + // listeners want a specific event type + listeners map[event.Type][]EventHandler + // ParseEventContent determines whether or not event content should be parsed before passing to handlers. + ParseEventContent bool + // ParseErrorHandler is called when event.Content.ParseRaw returns an error. + // If it returns false, the event will not be forwarded to listeners. + ParseErrorHandler func(evt *event.Event, err error) bool + // FilterJSON is used when the client starts syncing and doesn't get an existing filter ID from SyncStore's LoadFilterID. + FilterJSON *Filter +} + +var _ Syncer = (*DefaultSyncer)(nil) +var _ ExtensibleSyncer = (*DefaultSyncer)(nil) + +// NewDefaultSyncer returns an instantiated DefaultSyncer +func NewDefaultSyncer() *DefaultSyncer { + return &DefaultSyncer{ + listeners: make(map[event.Type][]EventHandler), + syncListeners: []SyncHandler{}, + globalListeners: []EventHandler{}, + ParseEventContent: true, + ParseErrorHandler: func(evt *event.Event, err error) bool { + // By default, drop known events that can't be parsed, but let unknown events through + return errors.Is(err, event.ErrUnsupportedContentType) || + // Also allow events that had their content already parsed by some other function + errors.Is(err, event.ErrContentAlreadyParsed) + }, + } +} + +// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of +// unrepeating events. Returns a fatal error if a listener panics. +func (s *DefaultSyncer) ProcessResponse(ctx context.Context, res *RespSync, since string) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("ProcessResponse panicked! since=%s panic=%s\n%s", since, r, debug.Stack()) + } + }() + ctx = context.WithValue(ctx, SyncTokenContextKey, since) + + for _, listener := range s.syncListeners { + if !listener(ctx, res, since) { + return + } + } + + s.processSyncEvents(ctx, "", res.ToDevice.Events, event.SourceToDevice, false) + s.processSyncEvents(ctx, "", res.Presence.Events, event.SourcePresence, false) + s.processSyncEvents(ctx, "", res.AccountData.Events, event.SourceAccountData, false) + + for roomID, roomData := range res.Rooms.Join { + if roomData.StateAfter == nil { + s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceJoin|event.SourceState, false) + s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceJoin|event.SourceTimeline, false) + } else { + s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceJoin|event.SourceTimeline, true) + s.processSyncEvents(ctx, roomID, roomData.StateAfter.Events, event.SourceJoin|event.SourceState, false) + } + s.processSyncEvents(ctx, roomID, roomData.Ephemeral.Events, event.SourceJoin|event.SourceEphemeral, false) + s.processSyncEvents(ctx, roomID, roomData.AccountData.Events, event.SourceJoin|event.SourceAccountData, false) + } + for roomID, roomData := range res.Rooms.Invite { + s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceInvite|event.SourceState, false) + } + for roomID, roomData := range res.Rooms.Leave { + s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceLeave|event.SourceState, false) + s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceLeave|event.SourceTimeline, false) + } + return +} + +func (s *DefaultSyncer) processSyncEvents(ctx context.Context, roomID id.RoomID, events []*event.Event, source event.Source, ignoreState bool) { + for _, evt := range events { + s.processSyncEvent(ctx, roomID, evt, source, ignoreState) + } +} + +func (s *DefaultSyncer) processSyncEvent(ctx context.Context, roomID id.RoomID, evt *event.Event, source event.Source, ignoreState bool) { + evt.RoomID = roomID + + // Ensure the type class is correct. It's safe to mutate the class since the event type is not a pointer. + // Listeners are keyed by type structs, which means only the correct class will pass. + switch { + case evt.StateKey != nil: + evt.Type.Class = event.StateEventType + case source == event.SourcePresence, source&event.SourceEphemeral != 0: + evt.Type.Class = event.EphemeralEventType + case source&event.SourceAccountData != 0: + evt.Type.Class = event.AccountDataEventType + case source == event.SourceToDevice: + evt.Type.Class = event.ToDeviceEventType + default: + evt.Type.Class = event.MessageEventType + } + + if s.ParseEventContent { + err := evt.Content.ParseRaw(evt.Type) + if err != nil && !s.ParseErrorHandler(evt, err) { + return + } + } + + evt.Mautrix.EventSource = source + evt.Mautrix.IgnoreState = ignoreState + s.Dispatch(ctx, evt) +} + +func (s *DefaultSyncer) Dispatch(ctx context.Context, evt *event.Event) { + for _, fn := range s.globalListeners { + fn(ctx, evt) + } + listeners, exists := s.listeners[evt.Type] + if exists { + for _, fn := range listeners { + fn(ctx, evt) + } + } +} + +// OnEventType allows callers to be notified when there are new events for the given event type. +// There are no duplicate checks. +func (s *DefaultSyncer) OnEventType(eventType event.Type, callback EventHandler) { + _, exists := s.listeners[eventType] + if !exists { + s.listeners[eventType] = []EventHandler{} + } + s.listeners[eventType] = append(s.listeners[eventType], callback) +} + +func (s *DefaultSyncer) OnSync(callback SyncHandler) { + s.syncListeners = append(s.syncListeners, callback) +} + +func (s *DefaultSyncer) OnEvent(callback EventHandler) { + s.globalListeners = append(s.globalListeners, callback) +} + +// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error. +func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) { + if errors.Is(err, MUnknownToken) { + return 0, err + } + return 10 * time.Second, nil +} + +var defaultFilter = Filter{ + Room: &RoomFilter{ + Timeline: &FilterPart{ + Limit: 50, + }, + }, +} + +// GetFilterJSON returns a filter with a timeline limit of 50. +func (s *DefaultSyncer) GetFilterJSON(userID id.UserID) *Filter { + if s.FilterJSON == nil { + defaultFilterCopy := defaultFilter + s.FilterJSON = &defaultFilterCopy + } + return s.FilterJSON +} + +// DontProcessOldEvents is a sync handler that removes rooms that the user just joined. +// It's meant for bots to ignore events from before the bot joined the room. +// +// To use it, register it with your Syncer, e.g.: +// +// cli.Syncer.(mautrix.ExtensibleSyncer).OnSync(cli.DontProcessOldEvents) +func (cli *Client) DontProcessOldEvents(_ context.Context, resp *RespSync, since string) bool { + return dontProcessOldEvents(cli.UserID, resp, since) +} + +var _ SyncHandler = (*Client)(nil).DontProcessOldEvents + +func dontProcessOldEvents(userID id.UserID, resp *RespSync, since string) bool { + if since == "" { + return false + } + // This is a horrible hack because /sync will return the most recent messages for a room + // as soon as you /join it. We do NOT want to process those events in that particular room + // because they may have already been processed (if you toggle the bot in/out of the room). + // + // Work around this by inspecting each room's timeline and seeing if an m.room.member event for us + // exists and is "join" and then discard processing that room entirely if so. + // TODO: We probably want to process messages from after the last join event in the timeline. + for roomID, roomData := range resp.Rooms.Join { + for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { + evt := roomData.Timeline.Events[i] + if evt.Type == event.StateMember && evt.GetStateKey() == string(userID) { + membership, _ := evt.Content.Raw["membership"].(string) + if membership == "join" { + _, ok := resp.Rooms.Join[roomID] + if !ok { + continue + } + delete(resp.Rooms.Join, roomID) // don't re-process messages + delete(resp.Rooms.Invite, roomID) // don't re-process invites + break + } + } + } + } + return true +} + +// MoveInviteState is a sync handler that moves events from the state event list to the InviteRoomState in the invite event. +// +// To use it, register it with your Syncer, e.g.: +// +// cli.Syncer.(mautrix.ExtensibleSyncer).OnSync(cli.MoveInviteState) +func (cli *Client) MoveInviteState(ctx context.Context, resp *RespSync, _ string) bool { + for _, meta := range resp.Rooms.Invite { + var inviteState []*event.Event + var inviteEvt *event.Event + for _, evt := range meta.State.Events { + if evt.Type == event.StateMember && evt.GetStateKey() == cli.UserID.String() { + inviteEvt = evt + } else { + evt.Type.Class = event.StateEventType + _ = evt.Content.ParseRaw(evt.Type) + inviteState = append(inviteState, evt) + } + } + if inviteEvt != nil { + inviteEvt.Unsigned.InviteRoomState = inviteState + meta.State.Events = []*event.Event{inviteEvt} + } + } + return true +} + +var _ SyncHandler = (*Client)(nil).MoveInviteState diff --git a/vendor/maunium.net/go/mautrix/syncstore.go b/vendor/maunium.net/go/mautrix/syncstore.go new file mode 100644 index 00000000..6c7fc9ee --- /dev/null +++ b/vendor/maunium.net/go/mautrix/syncstore.go @@ -0,0 +1,175 @@ +package mautrix + +import ( + "context" + "errors" + "fmt" + + "maunium.net/go/mautrix/id" +) + +var _ SyncStore = (*MemorySyncStore)(nil) +var _ SyncStore = (*AccountDataStore)(nil) + +// SyncStore is an interface which must be satisfied to store client data. +// +// You can either write a struct which persists this data to disk, or you can use the +// provided "MemorySyncStore" which just keeps data around in-memory which is lost on +// restarts. +type SyncStore interface { + SaveFilterID(ctx context.Context, userID id.UserID, filterID string) error + LoadFilterID(ctx context.Context, userID id.UserID) (string, error) + SaveNextBatch(ctx context.Context, userID id.UserID, nextBatchToken string) error + LoadNextBatch(ctx context.Context, userID id.UserID) (string, error) +} + +// Deprecated: renamed to SyncStore +type Storer = SyncStore + +// MemorySyncStore implements the Storer interface. +// +// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs +// or next batch tokens on any goroutine other than the syncing goroutine: the one +// which called Client.Sync(). +type MemorySyncStore struct { + Filters map[id.UserID]string + NextBatch map[id.UserID]string +} + +// SaveFilterID to memory. +func (s *MemorySyncStore) SaveFilterID(ctx context.Context, userID id.UserID, filterID string) error { + s.Filters[userID] = filterID + return nil +} + +// LoadFilterID from memory. +func (s *MemorySyncStore) LoadFilterID(ctx context.Context, userID id.UserID) (string, error) { + return s.Filters[userID], nil +} + +// SaveNextBatch to memory. +func (s *MemorySyncStore) SaveNextBatch(ctx context.Context, userID id.UserID, nextBatchToken string) error { + s.NextBatch[userID] = nextBatchToken + return nil +} + +// LoadNextBatch from memory. +func (s *MemorySyncStore) LoadNextBatch(ctx context.Context, userID id.UserID) (string, error) { + return s.NextBatch[userID], nil +} + +// NewMemorySyncStore constructs a new MemorySyncStore. +func NewMemorySyncStore() *MemorySyncStore { + return &MemorySyncStore{ + Filters: make(map[id.UserID]string), + NextBatch: make(map[id.UserID]string), + } +} + +// AccountDataStore uses account data to store the next batch token, and stores the filter ID in memory +// (as filters can be safely recreated every startup). +type AccountDataStore struct { + FilterID string + EventType string + client *Client + nextBatch string +} + +type accountData struct { + NextBatch string `json:"next_batch"` +} + +func (s *AccountDataStore) SaveFilterID(ctx context.Context, userID id.UserID, filterID string) error { + if userID.String() != s.client.UserID.String() { + panic("AccountDataStore must only be used with a single account") + } + s.FilterID = filterID + return nil +} + +func (s *AccountDataStore) LoadFilterID(ctx context.Context, userID id.UserID) (string, error) { + if userID.String() != s.client.UserID.String() { + panic("AccountDataStore must only be used with a single account") + } + return s.FilterID, nil +} + +func (s *AccountDataStore) SaveNextBatch(ctx context.Context, userID id.UserID, nextBatchToken string) error { + if userID.String() != s.client.UserID.String() { + panic("AccountDataStore must only be used with a single account") + } else if nextBatchToken == s.nextBatch { + return nil + } + + data := accountData{ + NextBatch: nextBatchToken, + } + + err := s.client.SetAccountData(ctx, s.EventType, data) + if err != nil { + return fmt.Errorf("failed to save next batch token to account data: %w", err) + } else { + s.client.Log.Debug(). + Str("old_token", s.nextBatch). + Str("new_token", nextBatchToken). + Msg("Saved next batch token") + s.nextBatch = nextBatchToken + } + return nil +} + +func (s *AccountDataStore) LoadNextBatch(ctx context.Context, userID id.UserID) (string, error) { + if userID.String() != s.client.UserID.String() { + panic("AccountDataStore must only be used with a single account") + } + + data := &accountData{} + + err := s.client.GetAccountData(ctx, s.EventType, data) + if err != nil { + if errors.Is(err, MNotFound) { + s.client.Log.Debug().Msg("No next batch token found in account data") + return "", nil + } else { + return "", fmt.Errorf("failed to load next batch token from account data: %w", err) + } + } + s.nextBatch = data.NextBatch + s.client.Log.Debug().Str("next_batch", data.NextBatch).Msg("Loaded next batch token from account data") + + return s.nextBatch, nil +} + +// NewAccountDataStore returns a new AccountDataStore, which stores +// the next_batch token as a custom event in account data in the +// homeserver. +// +// AccountDataStore is only appropriate for bots, not appservices. +// +// The event type should be a reversed DNS name like tld.domain.sub.internal and +// must be unique for a client. The data stored in it is considered internal +// and must not be modified through outside means. You should also add a filter +// for account data changes of this event type, to avoid ending up in a sync +// loop: +// +// filter := mautrix.Filter{ +// AccountData: mautrix.FilterPart{ +// Limit: 20, +// NotTypes: []event.Type{ +// event.NewEventType(eventType), +// }, +// }, +// } +// // If you use a custom Syncer, set the filter there, not like this +// client.Syncer.(*mautrix.DefaultSyncer).FilterJSON = &filter +// client.Store = mautrix.NewAccountDataStore("com.example.mybot.store", client) +// go func() { +// err := client.Sync() +// // don't forget to check err +// }() +func NewAccountDataStore(eventType string, client *Client) *AccountDataStore { + return &AccountDataStore{ + EventType: eventType, + client: client, + } +} diff --git a/vendor/maunium.net/go/mautrix/url.go b/vendor/maunium.net/go/mautrix/url.go new file mode 100644 index 00000000..d888956a --- /dev/null +++ b/vendor/maunium.net/go/mautrix/url.go @@ -0,0 +1,129 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mautrix + +import ( + "fmt" + "net/url" + "strconv" + "strings" +) + +func ParseAndNormalizeBaseURL(homeserverURL string) (*url.URL, error) { + hsURL, err := url.Parse(homeserverURL) + if err != nil { + return nil, err + } + if hsURL.Scheme == "" { + hsURL.Scheme = "https" + fixedURL := hsURL.String() + hsURL, err = url.Parse(fixedURL) + if err != nil { + return nil, fmt.Errorf("failed to parse fixed URL '%s': %v", fixedURL, err) + } + } + hsURL.RawPath = hsURL.EscapedPath() + return hsURL, nil +} + +// BuildURL builds a URL with the given path parts +func BuildURL(baseURL *url.URL, path ...any) *url.URL { + createdURL := *baseURL + rawParts := make([]string, len(path)+1) + rawParts[0] = strings.TrimSuffix(createdURL.RawPath, "/") + parts := make([]string, len(path)+1) + parts[0] = strings.TrimSuffix(createdURL.Path, "/") + for i, part := range path { + switch casted := part.(type) { + case string: + parts[i+1] = casted + case int: + parts[i+1] = strconv.Itoa(casted) + case fmt.Stringer: + parts[i+1] = casted.String() + default: + parts[i+1] = fmt.Sprint(casted) + } + rawParts[i+1] = url.PathEscape(parts[i+1]) + } + createdURL.Path = strings.Join(parts, "/") + createdURL.RawPath = strings.Join(rawParts, "/") + return &createdURL +} + +// BuildURL builds a URL with the Client's homeserver and appservice user ID set already. +func (cli *Client) BuildURL(urlPath PrefixableURLPath) string { + return cli.BuildURLWithFullQuery(urlPath, nil) +} + +// BuildClientURL builds a URL with the Client's homeserver and appservice user ID set already. +// This method also automatically prepends the client API prefix (/_matrix/client). +func (cli *Client) BuildClientURL(urlPath ...any) string { + return cli.BuildURLWithFullQuery(ClientURLPath(urlPath), nil) +} + +type PrefixableURLPath interface { + FullPath() []any +} + +type BaseURLPath []any + +func (bup BaseURLPath) FullPath() []any { + return bup +} + +type ClientURLPath []any + +func (cup ClientURLPath) FullPath() []any { + return append([]any{"_matrix", "client"}, []any(cup)...) +} + +type MediaURLPath []any + +func (mup MediaURLPath) FullPath() []any { + return append([]any{"_matrix", "media"}, []any(mup)...) +} + +type SynapseAdminURLPath []any + +func (saup SynapseAdminURLPath) FullPath() []any { + return append([]any{"_synapse", "admin"}, []any(saup)...) +} + +// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver +// and appservice user ID set already. +func (cli *Client) BuildURLWithQuery(urlPath PrefixableURLPath, urlQuery map[string]string) string { + return cli.BuildURLWithFullQuery(urlPath, func(q url.Values) { + if urlQuery != nil { + for k, v := range urlQuery { + q.Set(k, v) + } + } + }) +} + +// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver +// and appservice user ID set already. +func (cli *Client) BuildURLWithFullQuery(urlPath PrefixableURLPath, fn func(q url.Values)) string { + if cli == nil { + return "client is nil" + } + hsURL := *BuildURL(cli.HomeserverURL, urlPath.FullPath()...) + query := hsURL.Query() + if cli.SetAppServiceUserID { + query.Set("user_id", string(cli.UserID)) + } + if cli.SetAppServiceDeviceID && cli.DeviceID != "" { + query.Set("device_id", string(cli.DeviceID)) + query.Set("org.matrix.msc3202.device_id", string(cli.DeviceID)) + } + if fn != nil { + fn(query) + } + hsURL.RawQuery = query.Encode() + return hsURL.String() +} diff --git a/vendor/maunium.net/go/mautrix/version.go b/vendor/maunium.net/go/mautrix/version.go new file mode 100644 index 00000000..7b4eea41 --- /dev/null +++ b/vendor/maunium.net/go/mautrix/version.go @@ -0,0 +1,41 @@ +package mautrix + +import ( + "fmt" + "regexp" + "runtime" + "runtime/debug" + "strings" +) + +const Version = "v0.25.2" + +var GoModVersion = "" +var Commit = "" +var VersionWithCommit = Version + +var DefaultUserAgent = "mautrix-go/" + Version + " go/" + strings.TrimPrefix(runtime.Version(), "go") + +func init() { + if GoModVersion == "" { + info, _ := debug.ReadBuildInfo() + if info != nil { + for _, mod := range info.Deps { + if mod.Path == "maunium.net/go/mautrix" { + GoModVersion = mod.Version + break + } + } + } + } + if GoModVersion != "" { + match := regexp.MustCompile(`v.+\d{14}-([0-9a-f]{12})`).FindStringSubmatch(GoModVersion) + if match != nil { + Commit = match[1] + } + } + if Commit != "" { + VersionWithCommit = fmt.Sprintf("%s+dev.%s", Version, Commit[:8]) + DefaultUserAgent = strings.Replace(DefaultUserAgent, "mautrix-go/"+Version, "mautrix-go/"+VersionWithCommit, 1) + } +} diff --git a/vendor/maunium.net/go/mautrix/versions.go b/vendor/maunium.net/go/mautrix/versions.go new file mode 100644 index 00000000..0392532e --- /dev/null +++ b/vendor/maunium.net/go/mautrix/versions.go @@ -0,0 +1,207 @@ +// Copyright (c) 2022 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mautrix + +import ( + "fmt" + "regexp" + "strconv" +) + +// RespVersions is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientversions +type RespVersions struct { + Versions []SpecVersion `json:"versions"` + UnstableFeatures map[string]bool `json:"unstable_features"` +} + +func (versions *RespVersions) ContainsFunc(match func(found SpecVersion) bool) bool { + if versions == nil { + return false + } + for _, found := range versions.Versions { + if match(found) { + return true + } + } + return false +} + +func (versions *RespVersions) Contains(version SpecVersion) bool { + return versions.ContainsFunc(func(found SpecVersion) bool { + return found == version + }) +} + +func (versions *RespVersions) ContainsGreaterOrEqual(version SpecVersion) bool { + return versions.ContainsFunc(func(found SpecVersion) bool { + return found.GreaterThan(version) || found == version + }) +} + +func (versions *RespVersions) GetLatest() (latest SpecVersion) { + if versions == nil { + return + } + for _, ver := range versions.Versions { + if ver.GreaterThan(latest) { + latest = ver + } + } + return +} + +type UnstableFeature struct { + UnstableFlag string + SpecVersion SpecVersion +} + +var ( + FeatureAsyncUploads = UnstableFeature{UnstableFlag: "fi.mau.msc2246.stable", SpecVersion: SpecV17} + FeatureAppservicePing = UnstableFeature{UnstableFlag: "fi.mau.msc2659.stable", SpecVersion: SpecV17} + FeatureAuthenticatedMedia = UnstableFeature{UnstableFlag: "org.matrix.msc3916.stable", SpecVersion: SpecV111} + FeatureMutualRooms = UnstableFeature{UnstableFlag: "uk.half-shot.msc2666.query_mutual_rooms"} + FeatureUserRedaction = UnstableFeature{UnstableFlag: "org.matrix.msc4194"} + FeatureViewRedactedContent = UnstableFeature{UnstableFlag: "fi.mau.msc2815"} + FeatureAccountModeration = UnstableFeature{UnstableFlag: "uk.timedout.msc4323"} + FeatureUnstableProfileFields = UnstableFeature{UnstableFlag: "uk.tcpip.msc4133"} + FeatureArbitraryProfileFields = UnstableFeature{UnstableFlag: "uk.tcpip.msc4133.stable", SpecVersion: SpecV116} + + BeeperFeatureHungry = UnstableFeature{UnstableFlag: "com.beeper.hungry"} + BeeperFeatureBatchSending = UnstableFeature{UnstableFlag: "com.beeper.batch_sending"} + BeeperFeatureRoomYeeting = UnstableFeature{UnstableFlag: "com.beeper.room_yeeting"} + BeeperFeatureAutojoinInvites = UnstableFeature{UnstableFlag: "com.beeper.room_create_autojoin_invites"} + BeeperFeatureArbitraryProfileMeta = UnstableFeature{UnstableFlag: "com.beeper.arbitrary_profile_meta"} + BeeperFeatureAccountDataMute = UnstableFeature{UnstableFlag: "com.beeper.account_data_mute"} + BeeperFeatureInboxState = UnstableFeature{UnstableFlag: "com.beeper.inbox_state"} +) + +func (versions *RespVersions) Supports(feature UnstableFeature) bool { + if versions == nil { + return false + } + return versions.UnstableFeatures[feature.UnstableFlag] || + (!feature.SpecVersion.IsEmpty() && versions.ContainsGreaterOrEqual(feature.SpecVersion)) +} + +type SpecVersionFormat int + +const ( + SpecVersionFormatUnknown SpecVersionFormat = iota + SpecVersionFormatR + SpecVersionFormatV +) + +var ( + SpecR000 = MustParseSpecVersion("r0.0.0") + SpecR001 = MustParseSpecVersion("r0.0.1") + SpecR010 = MustParseSpecVersion("r0.1.0") + SpecR020 = MustParseSpecVersion("r0.2.0") + SpecR030 = MustParseSpecVersion("r0.3.0") + SpecR040 = MustParseSpecVersion("r0.4.0") + SpecR050 = MustParseSpecVersion("r0.5.0") + SpecR060 = MustParseSpecVersion("r0.6.0") + SpecR061 = MustParseSpecVersion("r0.6.1") + SpecV11 = MustParseSpecVersion("v1.1") + SpecV12 = MustParseSpecVersion("v1.2") + SpecV13 = MustParseSpecVersion("v1.3") + SpecV14 = MustParseSpecVersion("v1.4") + SpecV15 = MustParseSpecVersion("v1.5") + SpecV16 = MustParseSpecVersion("v1.6") + SpecV17 = MustParseSpecVersion("v1.7") + SpecV18 = MustParseSpecVersion("v1.8") + SpecV19 = MustParseSpecVersion("v1.9") + SpecV110 = MustParseSpecVersion("v1.10") + SpecV111 = MustParseSpecVersion("v1.11") + SpecV112 = MustParseSpecVersion("v1.12") + SpecV113 = MustParseSpecVersion("v1.13") + SpecV114 = MustParseSpecVersion("v1.14") + SpecV115 = MustParseSpecVersion("v1.15") + SpecV116 = MustParseSpecVersion("v1.16") +) + +func (svf SpecVersionFormat) String() string { + switch svf { + case SpecVersionFormatR: + return "r" + case SpecVersionFormatV: + return "v" + default: + return "" + } +} + +type SpecVersion struct { + Format SpecVersionFormat + Major int + Minor int + Patch int + + Raw string +} + +var legacyVersionRegex = regexp.MustCompile(`^r(\d+)\.(\d+)\.(\d+)$`) +var modernVersionRegex = regexp.MustCompile(`^v(\d+)\.(\d+)$`) + +func MustParseSpecVersion(version string) SpecVersion { + sv, err := ParseSpecVersion(version) + if err != nil { + panic(err) + } + return sv +} + +func ParseSpecVersion(version string) (sv SpecVersion, err error) { + sv.Raw = version + if parts := modernVersionRegex.FindStringSubmatch(version); parts != nil { + sv.Major, _ = strconv.Atoi(parts[1]) + sv.Minor, _ = strconv.Atoi(parts[2]) + sv.Format = SpecVersionFormatV + } else if parts = legacyVersionRegex.FindStringSubmatch(version); parts != nil { + sv.Major, _ = strconv.Atoi(parts[1]) + sv.Minor, _ = strconv.Atoi(parts[2]) + sv.Patch, _ = strconv.Atoi(parts[3]) + sv.Format = SpecVersionFormatR + } else { + err = fmt.Errorf("version '%s' doesn't match either known syntax", version) + } + return +} + +func (sv *SpecVersion) UnmarshalText(version []byte) error { + *sv, _ = ParseSpecVersion(string(version)) + return nil +} + +func (sv *SpecVersion) MarshalText() ([]byte, error) { + return []byte(sv.String()), nil +} + +func (sv SpecVersion) String() string { + switch sv.Format { + case SpecVersionFormatR: + return fmt.Sprintf("r%d.%d.%d", sv.Major, sv.Minor, sv.Patch) + case SpecVersionFormatV: + return fmt.Sprintf("v%d.%d", sv.Major, sv.Minor) + default: + return sv.Raw + } +} + +func (sv SpecVersion) IsEmpty() bool { + return sv.Format == SpecVersionFormatUnknown && sv.Raw == "" +} + +func (sv SpecVersion) LessThan(other SpecVersion) bool { + return sv != other && !sv.GreaterThan(other) +} + +func (sv SpecVersion) GreaterThan(other SpecVersion) bool { + return sv.Format > other.Format || + (sv.Format == other.Format && sv.Major > other.Major) || + (sv.Format == other.Format && sv.Major == other.Major && sv.Minor > other.Minor) || + (sv.Format == other.Format && sv.Major == other.Major && sv.Minor == other.Minor && sv.Patch > other.Patch) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index beaff9b8..5b990607 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,10 @@ # dario.cat/mergo v1.0.2 ## explicit; go 1.13 dario.cat/mergo +# filippo.io/edwards25519 v1.1.0 +## explicit; go 1.20 +filippo.io/edwards25519 +filippo.io/edwards25519/field # github.com/AlecAivazis/survey/v2 v2.3.7 ## explicit; go 1.13 github.com/AlecAivazis/survey/v2 @@ -281,13 +285,10 @@ github.com/mailru/easyjson/jwriter # github.com/matcornic/hermes/v2 v2.1.0 ## explicit; go 1.13 github.com/matcornic/hermes/v2 -# github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 -## explicit; go 1.12 -github.com/matrix-org/gomatrix -# github.com/mattn/go-colorable v0.1.13 -## explicit; go 1.15 +# github.com/mattn/go-colorable v0.1.14 +## explicit; go 1.18 github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.19 +# github.com/mattn/go-isatty v0.0.20 ## explicit; go 1.15 github.com/mattn/go-isatty # github.com/mattn/go-runewidth v0.0.16 @@ -400,9 +401,18 @@ github.com/ssor/bom github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml github.com/stretchr/testify/require +# github.com/tidwall/gjson v1.18.0 +## explicit; go 1.12 +github.com/tidwall/gjson +# github.com/tidwall/match v1.2.0 +## explicit; go 1.15 +github.com/tidwall/match # github.com/tidwall/pretty v1.2.1 ## explicit; go 1.16 github.com/tidwall/pretty +# github.com/tidwall/sjson v1.2.5 +## explicit; go 1.14 +github.com/tidwall/sjson # github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 ## explicit; go 1.16 github.com/tonistiigi/go-csvvalue @@ -421,6 +431,20 @@ go.etcd.io/bbolt go.etcd.io/bbolt/errors go.etcd.io/bbolt/internal/common go.etcd.io/bbolt/internal/freelist +# go.mau.fi/util v0.9.2 +## explicit; go 1.24.0 +go.mau.fi/util/base58 +go.mau.fi/util/exbytes +go.mau.fi/util/exerrors +go.mau.fi/util/exgjson +go.mau.fi/util/exhttp +go.mau.fi/util/exmaps +go.mau.fi/util/glob +go.mau.fi/util/jsonbytes +go.mau.fi/util/jsontime +go.mau.fi/util/ptr +go.mau.fi/util/random +go.mau.fi/util/retryafter # go.opentelemetry.io/auto/sdk v1.1.0 ## explicit; go 1.22.0 go.opentelemetry.io/auto/sdk @@ -504,16 +528,22 @@ go.podman.io/storage/pkg/reexec go.podman.io/storage/pkg/regexp go.podman.io/storage/pkg/system go.podman.io/storage/pkg/unshare -# golang.org/x/crypto v0.41.0 -## explicit; go 1.23.0 +# golang.org/x/crypto v0.43.0 +## explicit; go 1.24.0 +golang.org/x/crypto/curve25519 +golang.org/x/crypto/hkdf golang.org/x/crypto/pbkdf2 golang.org/x/crypto/scrypt golang.org/x/crypto/sha3 -# golang.org/x/mod v0.27.0 -## explicit; go 1.23.0 +# golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 +## explicit; go 1.24.0 +golang.org/x/exp/constraints +golang.org/x/exp/maps +# golang.org/x/mod v0.29.0 +## explicit; go 1.24.0 golang.org/x/mod/semver -# golang.org/x/net v0.43.0 -## explicit; go 1.23.0 +# golang.org/x/net v0.46.0 +## explicit; go 1.24.0 golang.org/x/net/html golang.org/x/net/html/atom golang.org/x/net/http/httpguts @@ -529,21 +559,21 @@ golang.org/x/net/trace ## explicit; go 1.23.0 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.16.0 -## explicit; go 1.23.0 +# golang.org/x/sync v0.17.0 +## explicit; go 1.24.0 golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.35.0 -## explicit; go 1.23.0 +# golang.org/x/sys v0.38.0 +## explicit; go 1.24.0 golang.org/x/sys/cpu golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.34.0 -## explicit; go 1.23.0 +# golang.org/x/term v0.36.0 +## explicit; go 1.24.0 golang.org/x/term -# golang.org/x/text v0.28.0 -## explicit; go 1.23.0 +# golang.org/x/text v0.30.0 +## explicit; go 1.24.0 golang.org/x/text/cases golang.org/x/text/internal golang.org/x/text/internal/language @@ -626,7 +656,7 @@ google.golang.org/grpc/tap # google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 ## explicit; go 1.21 google.golang.org/grpc/cmd/protoc-gen-go-grpc -# google.golang.org/protobuf v1.36.8 +# google.golang.org/protobuf v1.36.10 ## explicit; go 1.23 google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo @@ -969,6 +999,25 @@ k8s.io/utils/clock k8s.io/utils/internal/third_party/forked/golang/net k8s.io/utils/net k8s.io/utils/ptr +# maunium.net/go/mautrix v0.25.2 +## explicit; go 1.24.0 +maunium.net/go/mautrix +maunium.net/go/mautrix/crypto/aescbc +maunium.net/go/mautrix/crypto/attachment +maunium.net/go/mautrix/crypto/backup +maunium.net/go/mautrix/crypto/canonicaljson +maunium.net/go/mautrix/crypto/ed25519 +maunium.net/go/mautrix/crypto/goolm/aessha2 +maunium.net/go/mautrix/crypto/goolm/crypto +maunium.net/go/mautrix/crypto/goolm/goolmbase64 +maunium.net/go/mautrix/crypto/goolm/libolmpickle +maunium.net/go/mautrix/crypto/olm +maunium.net/go/mautrix/crypto/pkcs7 +maunium.net/go/mautrix/crypto/signatures +maunium.net/go/mautrix/crypto/utils +maunium.net/go/mautrix/event +maunium.net/go/mautrix/id +maunium.net/go/mautrix/pushrules # sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 ## explicit; go 1.21 sigs.k8s.io/json