commit e175faed7efe1852705e0be9ef57afcf3be580c6 Author: 李伟乐 Date: Fri Feb 24 13:31:01 2023 +0800 first commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/go-common.iml b/.idea/go-common.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/go-common.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3ce3588 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..4833062 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/captcha/captcha.go b/captcha/captcha.go new file mode 100644 index 0000000..12e741d --- /dev/null +++ b/captcha/captcha.go @@ -0,0 +1,32 @@ +package captcha + +import ( + "github.com/mojocn/base64Captcha" +) + +func GetCaptcha() (string, string) { + //config struct for Character + //字符,公式,验证码配置 + var configC = base64Captcha.ConfigCharacter{ + Height: 30, + Width: 80, + //const CaptchaModeNumber:数字,CaptchaModeAlphabet:字母,CaptchaModeArithmetic:算术,CaptchaModeNumberAlphabet:数字字母混合. + Mode: base64Captcha.CaptchaModeNumber, + ComplexOfNoiseText: base64Captcha.CaptchaComplexLower, + ComplexOfNoiseDot: base64Captcha.CaptchaComplexLower, + IsUseSimpleFont: true, + IsShowHollowLine: false, + IsShowNoiseDot: false, + IsShowNoiseText: false, + IsShowSlimeLine: false, + IsShowSineLine: false, + CaptchaLen: 4, + } + idKeyC, capC := base64Captcha.GenerateCaptcha("", configC) + base64stringC := base64Captcha.CaptchaWriteToBase64Encoding(capC) + return idKeyC, base64stringC +} + +func Verify(idKey, verifyValue string) bool { + return base64Captcha.VerifyCaptcha(idKey, verifyValue) +} diff --git a/consumer/consumer.go b/consumer/consumer.go new file mode 100644 index 0000000..53c5251 --- /dev/null +++ b/consumer/consumer.go @@ -0,0 +1,72 @@ +package consumer + +import ( + "github.com/nsqio/go-nsq" + "sync" +) + +type baseConsumerService struct { + consumers []*BaseConsumer +} + +func New() *baseConsumerService { + return &baseConsumerService{ + consumers: []*BaseConsumer{}, + } +} +func (s *baseConsumerService) Register(b *BaseConsumer) { + s.consumers = append(s.consumers, b) +} +func (s *baseConsumerService) Run() error { + for _, v := range s.consumers { + if err := v.Run(); err != nil { + return err + } + } + return nil +} + +func (s *baseConsumerService) Stop() error { + for _, v := range s.consumers { + if err := v.Stop(); err != nil { + return err + } + } + return nil +} + +type BaseConsumer struct { + host, topic, consumerChan string + wg *sync.WaitGroup + consumer *nsq.Consumer +} + +func (s *BaseConsumer) Stop() error { + s.consumer.Stop() + s.wg.Wait() + return nil +} +func (s *BaseConsumer) Run() error { + return s.consumer.ConnectToNSQD(s.host) +} + +func NewBaseConsumer(host, topic, consumerChan string, fun func(msg *nsq.Message) error) *BaseConsumer { + resp := &BaseConsumer{ + wg: &sync.WaitGroup{}, + consumer: nil, + host: host, + topic: topic, + consumerChan: consumerChan, + } + consumer, err := nsq.NewConsumer(topic, consumerChan, nsq.NewConfig()) + if err != nil { + panic(err) + } + consumer.AddHandler(nsq.HandlerFunc(func(msg *nsq.Message) error { + resp.wg.Add(1) + defer resp.wg.Done() + return fun(msg) + })) + resp.consumer = consumer + return resp +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..581f6ff --- /dev/null +++ b/go.mod @@ -0,0 +1,41 @@ +module git.diulo.com/mogfee/go-common + +go 1.20 + +require ( + github.com/PuerkitoBio/goquery v1.8.1 + github.com/gin-gonic/gin v1.9.0 + github.com/mojocn/base64Captcha v1.2.2 + github.com/nsqio/go-nsq v1.1.0 + github.com/pkg/errors v0.9.1 +) + +require ( + github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/bytedance/sonic v1.8.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.11.2 // indirect + github.com/goccy/go-json v0.10.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.9 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/crypto v0.5.0 // indirect + golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..be33b4d --- /dev/null +++ b/go.sum @@ -0,0 +1,124 @@ +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= +github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mojocn/base64Captcha v1.2.2 h1:NTFnThPVrb3tR66JO/N8/ZHsyFrNc7ho+xRpxBUEIlo= +github.com/mojocn/base64Captcha v1.2.2/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= +github.com/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE= +github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= +github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/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= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/php_session_decoder/common.go b/php_session_decoder/common.go new file mode 100644 index 0000000..061e750 --- /dev/null +++ b/php_session_decoder/common.go @@ -0,0 +1,7 @@ +package php_session_decoder + +import "git.diulo.com/mogfee/go-common/php_session_decoder/php_serialize" + +const SEPARATOR_VALUE_NAME rune = '|' + +type PhpSession map[string]php_serialize.PhpValue diff --git a/php_session_decoder/decoder.go b/php_session_decoder/decoder.go new file mode 100644 index 0000000..1b72bae --- /dev/null +++ b/php_session_decoder/decoder.go @@ -0,0 +1,66 @@ +package php_session_decoder + +import ( + "bytes" + "git.diulo.com/mogfee/go-common/php_session_decoder/php_serialize" + "io" + "strings" +) + +type PhpDecoder struct { + source *strings.Reader + decoder *php_serialize.UnSerializer +} + +func NewPhpDecoder(phpSession string) *PhpDecoder { + decoder := &PhpDecoder{ + source: strings.NewReader(phpSession), + decoder: php_serialize.NewUnSerializer(""), + } + decoder.decoder.SetReader(decoder.source) + return decoder +} + +func (self *PhpDecoder) SetSerializedDecodeFunc(f php_serialize.SerializedDecodeFunc) { + self.decoder.SetSerializedDecodeFunc(f) +} + +func (self *PhpDecoder) Decode() (PhpSession, error) { + var ( + name string + err error + value php_serialize.PhpValue + ) + res := make(PhpSession) + + for { + if name, err = self.readName(); err != nil { + break + } + if value, err = self.decoder.Decode(); err != nil { + break + } + res[name] = value + } + + if err == io.EOF { + err = nil + } + return res, err +} + +func (self *PhpDecoder) readName() (string, error) { + var ( + token rune + err error + ) + buf := bytes.NewBuffer([]byte{}) + for { + if token, _, err = self.source.ReadRune(); err != nil || token == SEPARATOR_VALUE_NAME { + break + } else { + buf.WriteRune(token) + } + } + return buf.String(), err +} diff --git a/php_session_decoder/encoder.go b/php_session_decoder/encoder.go new file mode 100644 index 0000000..fe2d3dc --- /dev/null +++ b/php_session_decoder/encoder.go @@ -0,0 +1,46 @@ +package php_session_decoder + +import ( + "bytes" + "fmt" + "git.diulo.com/mogfee/go-common/php_session_decoder/php_serialize" +) + +type PhpEncoder struct { + data PhpSession + encoder *php_serialize.Serializer +} + +func NewPhpEncoder(data PhpSession) *PhpEncoder { + return &PhpEncoder{ + data: data, + encoder: php_serialize.NewSerializer(), + } +} + +func (self *PhpEncoder) SetSerializedEncodeFunc(f php_serialize.SerializedEncodeFunc) { + self.encoder.SetSerializedEncodeFunc(f) +} + +func (self *PhpEncoder) Encode() (string, error) { + if self.data == nil { + return "", nil + } + var ( + err error + val string + ) + buf := bytes.NewBuffer([]byte{}) + + for k, v := range self.data { + buf.WriteString(k) + buf.WriteRune(SEPARATOR_VALUE_NAME) + if val, err = self.encoder.Encode(v); err != nil { + err = fmt.Errorf("php_session: error during encode value for %q: %v", k, err) + break + } + buf.WriteString(val) + } + + return buf.String(), err +} diff --git a/php_session_decoder/php_serialize/common.go b/php_session_decoder/php_serialize/common.go new file mode 100644 index 0000000..e5dd462 --- /dev/null +++ b/php_session_decoder/php_serialize/common.go @@ -0,0 +1,189 @@ +package php_serialize + +const ( + TOKEN_NULL rune = 'N' + TOKEN_BOOL rune = 'b' + TOKEN_INT rune = 'i' + TOKEN_FLOAT rune = 'd' + TOKEN_STRING rune = 's' + TOKEN_ARRAY rune = 'a' + TOKEN_OBJECT rune = 'O' + TOKEN_OBJECT_SERIALIZED rune = 'C' + TOKEN_REFERENCE rune = 'R' + TOKEN_REFERENCE_OBJECT rune = 'r' + TOKEN_SPL_ARRAY rune = 'x' + TOKEN_SPL_ARRAY_MEMBERS rune = 'm' + + SEPARATOR_VALUE_TYPE rune = ':' + SEPARATOR_VALUES rune = ';' + + DELIMITER_STRING_LEFT rune = '"' + DELIMITER_STRING_RIGHT rune = '"' + DELIMITER_OBJECT_LEFT rune = '{' + DELIMITER_OBJECT_RIGHT rune = '}' + + FORMATTER_FLOAT byte = 'g' + FORMATTER_PRECISION int = 17 +) + +var ( + debugMode = false +) + +func Debug(value bool) { + debugMode = value +} + +func NewPhpObject(className string) *PhpObject { + return &PhpObject{ + className: className, + members: PhpArray{}, + } +} + +type SerializedDecodeFunc func(string) (PhpValue, error) + +type SerializedEncodeFunc func(PhpValue) (string, error) + +type PhpValue interface{} + +type PhpArray map[PhpValue]PhpValue + +type PhpSlice []PhpValue + +type PhpObject struct { + className string + members PhpArray +} + +func (self *PhpObject) GetClassName() string { + return self.className +} + +func (self *PhpObject) SetClassName(name string) *PhpObject { + self.className = name + return self +} + +func (self *PhpObject) GetMembers() PhpArray { + return self.members +} + +func (self *PhpObject) SetMembers(members PhpArray) *PhpObject { + self.members = members + return self +} + +func (self *PhpObject) GetPrivate(name string) (v PhpValue, ok bool) { + v, ok = self.members["\x00"+self.className+"\x00"+name] + return +} + +func (self *PhpObject) SetPrivate(name string, value PhpValue) *PhpObject { + self.members["\x00"+self.className+"\x00"+name] = value + return self +} + +func (self *PhpObject) GetProtected(name string) (v PhpValue, ok bool) { + v, ok = self.members["\x00*\x00"+name] + return +} + +func (self *PhpObject) SetProtected(name string, value PhpValue) *PhpObject { + self.members["\x00*\x00"+name] = value + return self +} + +func (self *PhpObject) GetPublic(name string) (v PhpValue, ok bool) { + v, ok = self.members[name] + return +} + +func (self *PhpObject) SetPublic(name string, value PhpValue) *PhpObject { + self.members[name] = value + return self +} + +func NewPhpObjectSerialized(className string) *PhpObjectSerialized { + return &PhpObjectSerialized{ + className: className, + } +} + +type PhpObjectSerialized struct { + className string + data string + value PhpValue +} + +func (self *PhpObjectSerialized) GetClassName() string { + return self.className +} + +func (self *PhpObjectSerialized) SetClassName(name string) *PhpObjectSerialized { + self.className = name + return self +} + +func (self *PhpObjectSerialized) GetData() string { + return self.data +} + +func (self *PhpObjectSerialized) SetData(data string) *PhpObjectSerialized { + self.data = data + return self +} + +func (self *PhpObjectSerialized) GetValue() PhpValue { + return self.value +} + +func (self *PhpObjectSerialized) SetValue(value PhpValue) *PhpObjectSerialized { + self.value = value + return self +} + +func NewPhpSplArray(array, properties PhpValue) *PhpSplArray { + if array == nil { + array = make(PhpArray) + } + + if properties == nil { + properties = make(PhpArray) + } + + return &PhpSplArray{ + array: array, + properties: properties, + } +} + +type PhpSplArray struct { + flags int + array PhpValue + properties PhpValue +} + +func (self *PhpSplArray) GetFlags() int { + return self.flags +} + +func (self *PhpSplArray) SetFlags(value int) { + self.flags = value +} + +func (self *PhpSplArray) GetArray() PhpValue { + return self.array +} + +func (self *PhpSplArray) SetArray(value PhpValue) { + self.array = value +} + +func (self *PhpSplArray) GetProperties() PhpValue { + return self.properties +} + +func (self *PhpSplArray) SetProperties(value PhpValue) { + self.properties = value +} diff --git a/php_session_decoder/php_serialize/common_helper.go b/php_session_decoder/php_serialize/common_helper.go new file mode 100644 index 0000000..9e95356 --- /dev/null +++ b/php_session_decoder/php_serialize/common_helper.go @@ -0,0 +1,91 @@ +package php_serialize + +import ( + "strconv" +) + +func PhpValueString(p PhpValue) (res string) { + res, _ = p.(string) + return +} + +func PhpValueBool(p PhpValue) (res bool) { + switch v := p.(type) { + case bool: + res = v + case string: + res, _ = strconv.ParseBool(v) + } + return +} + +func PhpValueInt(p PhpValue) (res int) { + switch intVal := p.(type) { + case int: + res = intVal + case int8: + res = int(intVal) + case int16: + res = int(intVal) + case int32: + res = int(intVal) + case int64: + res = int(intVal) + case uint: + res = int(intVal) + case uint8: + res = int(intVal) + case uint16: + res = int(intVal) + case uint32: + res = int(intVal) + case uint64: + res = int(intVal) + case string: + str, _ := p.(string) + res, _ = strconv.Atoi(str) + } + return +} + +func PhpValueInt64(p PhpValue) (res int64) { + switch v := p.(type) { + case int64: + res = v + default: + res = int64(PhpValueInt(v)) + } + return +} + +func PhpValueUInt(p PhpValue) (res uint) { + switch v := p.(type) { + case uint: + res = v + default: + res = uint(PhpValueInt(v)) + } + return +} + +func PhpValueUInt64(p PhpValue) (res uint64) { + switch v := p.(type) { + case uint64: + res = v + default: + res = uint64(PhpValueInt(v)) + } + return +} + +func PhpValueFloat64(p PhpValue) (res float64) { + switch v := p.(type) { + case float64: + res = v + case string: + res, _ = strconv.ParseFloat(v, 64) + default: + return float64(PhpValueInt(p)) + } + return +} diff --git a/php_session_decoder/php_serialize/serialize.go b/php_session_decoder/php_serialize/serialize.go new file mode 100644 index 0000000..bb1c673 --- /dev/null +++ b/php_session_decoder/php_serialize/serialize.go @@ -0,0 +1,264 @@ +package php_serialize + +import ( + "bytes" + "fmt" + "strconv" +) + +func Serialize(v PhpValue) (string, error) { + encoder := NewSerializer() + encoder.SetSerializedEncodeFunc(SerializedEncodeFunc(Serialize)) + return encoder.Encode(v) +} + +type Serializer struct { + lastErr error + encodeFunc SerializedEncodeFunc +} + +func NewSerializer() *Serializer { + return &Serializer{} +} + +func (self *Serializer) SetSerializedEncodeFunc(f SerializedEncodeFunc) { + self.encodeFunc = f +} + +func (self *Serializer) Encode(v PhpValue) (string, error) { + var value bytes.Buffer + + switch t := v.(type) { + default: + self.saveError(fmt.Errorf("php_serialize: Unknown type %T with value %#v", t, v)) + case nil: + value = self.encodeNull() + case bool: + value = self.encodeBool(v) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: + value = self.encodeNumber(v) + case string: + value = self.encodeString(v, DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, true) + case PhpArray, map[PhpValue]PhpValue, PhpSlice: + value = self.encodeArray(v, true) + case *PhpObject: + value = self.encodeObject(v) + case *PhpObjectSerialized: + value = self.encodeSerialized(v) + case *PhpSplArray: + value = self.encodeSplArray(v) + } + + return value.String(), self.lastErr +} + +func (self *Serializer) encodeNull() (buffer bytes.Buffer) { + buffer.WriteRune(TOKEN_NULL) + buffer.WriteRune(SEPARATOR_VALUES) + return +} + +func (self *Serializer) encodeBool(v PhpValue) (buffer bytes.Buffer) { + buffer.WriteRune(TOKEN_BOOL) + buffer.WriteRune(SEPARATOR_VALUE_TYPE) + + if bVal, ok := v.(bool); ok && bVal { + buffer.WriteString("1") + } else { + buffer.WriteString("0") + } + + buffer.WriteRune(SEPARATOR_VALUES) + return +} + +func (self *Serializer) encodeNumber(v PhpValue) (buffer bytes.Buffer) { + var val string + + isFloat := false + + switch intVal := v.(type) { + default: + val = "0" + case int: + val = strconv.FormatInt(int64(intVal), 10) + case int8: + val = strconv.FormatInt(int64(intVal), 10) + case int16: + val = strconv.FormatInt(int64(intVal), 10) + case int32: + val = strconv.FormatInt(int64(intVal), 10) + case int64: + val = strconv.FormatInt(int64(intVal), 10) + case uint: + val = strconv.FormatUint(uint64(intVal), 10) + case uint8: + val = strconv.FormatUint(uint64(intVal), 10) + case uint16: + val = strconv.FormatUint(uint64(intVal), 10) + case uint32: + val = strconv.FormatUint(uint64(intVal), 10) + case uint64: + val = strconv.FormatUint(uint64(intVal), 10) + // PHP has precision = 17 by default + case float32: + val = strconv.FormatFloat(float64(intVal), FORMATTER_FLOAT, FORMATTER_PRECISION, 32) + isFloat = true + case float64: + val = strconv.FormatFloat(float64(intVal), FORMATTER_FLOAT, FORMATTER_PRECISION, 64) + isFloat = true + } + + if isFloat { + buffer.WriteRune(TOKEN_FLOAT) + } else { + buffer.WriteRune(TOKEN_INT) + } + + buffer.WriteRune(SEPARATOR_VALUE_TYPE) + buffer.WriteString(val) + buffer.WriteRune(SEPARATOR_VALUES) + + return +} + +func (self *Serializer) encodeString(v PhpValue, left, right rune, isFinal bool) (buffer bytes.Buffer) { + val, _ := v.(string) + + if isFinal { + buffer.WriteRune(TOKEN_STRING) + } + + buffer.WriteString(self.prepareLen(len(val))) + buffer.WriteRune(left) + buffer.WriteString(val) + buffer.WriteRune(right) + + if isFinal { + buffer.WriteRune(SEPARATOR_VALUES) + } + + return +} + +func (self *Serializer) encodeArray(v PhpValue, isFinal bool) (buffer bytes.Buffer) { + var ( + arrLen int + s string + ) + + if isFinal { + buffer.WriteRune(TOKEN_ARRAY) + } + + switch arrVal := v.(type) { + case PhpArray: + arrLen = len(arrVal) + buffer.WriteString(self.prepareLen(arrLen)) + buffer.WriteRune(DELIMITER_OBJECT_LEFT) + + for k, v := range arrVal { + s, _ = self.Encode(k) + buffer.WriteString(s) + s, _ = self.Encode(v) + buffer.WriteString(s) + } + + case map[PhpValue]PhpValue: + arrLen = len(arrVal) + + buffer.WriteString(self.prepareLen(arrLen)) + buffer.WriteRune(DELIMITER_OBJECT_LEFT) + + for k, v := range arrVal { + s, _ = self.Encode(k) + buffer.WriteString(s) + s, _ = self.Encode(v) + buffer.WriteString(s) + } + case PhpSlice: + arrLen = len(arrVal) + + buffer.WriteString(self.prepareLen(arrLen)) + buffer.WriteRune(DELIMITER_OBJECT_LEFT) + + for k, v := range arrVal { + s, _ = self.Encode(k) + buffer.WriteString(s) + s, _ = self.Encode(v) + buffer.WriteString(s) + } + } + + buffer.WriteRune(DELIMITER_OBJECT_RIGHT) + + return +} + +func (self *Serializer) encodeObject(v PhpValue) (buffer bytes.Buffer) { + obj, _ := v.(*PhpObject) + buffer.WriteRune(TOKEN_OBJECT) + buffer.WriteString(self.prepareClassName(obj.className)) + encoded := self.encodeArray(obj.members, false) + buffer.WriteString(encoded.String()) + return +} + +func (self *Serializer) encodeSerialized(v PhpValue) (buffer bytes.Buffer) { + var serialized string + + obj, _ := v.(*PhpObjectSerialized) + buffer.WriteRune(TOKEN_OBJECT_SERIALIZED) + buffer.WriteString(self.prepareClassName(obj.className)) + + if self.encodeFunc == nil { + serialized = obj.GetData() + } else { + var err error + if serialized, err = self.encodeFunc(obj.GetValue()); err != nil { + self.saveError(err) + } + } + + encoded := self.encodeString(serialized, DELIMITER_OBJECT_LEFT, DELIMITER_OBJECT_RIGHT, false) + buffer.WriteString(encoded.String()) + return +} + +func (self *Serializer) encodeSplArray(v PhpValue) bytes.Buffer { + var buffer bytes.Buffer + obj, _ := v.(*PhpSplArray) + + buffer.WriteRune(TOKEN_SPL_ARRAY) + buffer.WriteRune(SEPARATOR_VALUE_TYPE) + + encoded := self.encodeNumber(obj.flags) + buffer.WriteString(encoded.String()) + + data, _ := self.Encode(obj.array) + buffer.WriteString(data) + + buffer.WriteRune(SEPARATOR_VALUES) + buffer.WriteRune(TOKEN_SPL_ARRAY_MEMBERS) + buffer.WriteRune(SEPARATOR_VALUE_TYPE) + + data, _ = self.Encode(obj.properties) + buffer.WriteString(data) + + return buffer +} + +func (self *Serializer) prepareLen(l int) string { + return string(SEPARATOR_VALUE_TYPE) + strconv.Itoa(l) + string(SEPARATOR_VALUE_TYPE) +} + +func (self *Serializer) prepareClassName(name string) string { + encoded := self.encodeString(name, DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, false) + return encoded.String() +} + +func (self *Serializer) saveError(err error) { + if self.lastErr == nil { + self.lastErr = err + } +} diff --git a/php_session_decoder/php_serialize/unserialize.go b/php_session_decoder/php_serialize/unserialize.go new file mode 100644 index 0000000..9ef3340 --- /dev/null +++ b/php_session_decoder/php_serialize/unserialize.go @@ -0,0 +1,315 @@ +package php_serialize + +import ( + "bytes" + "fmt" + "log" + "strconv" + "strings" +) + +const UNSERIALIZABLE_OBJECT_MAX_LEN = 10 * 1024 * 1024 * 1024 + +func UnSerialize(s string) (PhpValue, error) { + decoder := NewUnSerializer(s) + decoder.SetSerializedDecodeFunc(SerializedDecodeFunc(UnSerialize)) + return decoder.Decode() +} + +type UnSerializer struct { + source string + r *strings.Reader + lastErr error + decodeFunc SerializedDecodeFunc +} + +func NewUnSerializer(data string) *UnSerializer { + return &UnSerializer{ + source: data, + } +} + +func (self *UnSerializer) SetReader(r *strings.Reader) { + self.r = r +} + +func (self *UnSerializer) SetSerializedDecodeFunc(f SerializedDecodeFunc) { + self.decodeFunc = f +} + +func (self *UnSerializer) Decode() (PhpValue, error) { + if self.r == nil { + self.r = strings.NewReader(self.source) + } + + var value PhpValue + + if token, _, err := self.r.ReadRune(); err == nil { + switch token { + default: + self.saveError(fmt.Errorf("php_serialize: Unknown token %#U", token)) + case TOKEN_NULL: + value = self.decodeNull() + case TOKEN_BOOL: + value = self.decodeBool() + case TOKEN_INT: + value = self.decodeNumber(false) + case TOKEN_FLOAT: + value = self.decodeNumber(true) + case TOKEN_STRING: + value = self.decodeString(DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, true) + case TOKEN_ARRAY: + value = self.decodeArray() + case TOKEN_OBJECT: + value = self.decodeObject() + case TOKEN_OBJECT_SERIALIZED: + value = self.decodeSerialized() + case TOKEN_REFERENCE, TOKEN_REFERENCE_OBJECT: + value = self.decodeReference() + case TOKEN_SPL_ARRAY: + value = self.decodeSplArray() + + } + } + + return value, self.lastErr +} + +func (self *UnSerializer) decodeNull() PhpValue { + self.expect(SEPARATOR_VALUES) + return nil +} + +func (self *UnSerializer) decodeBool() PhpValue { + var ( + raw rune + err error + ) + self.expect(SEPARATOR_VALUE_TYPE) + + if raw, _, err = self.r.ReadRune(); err != nil { + self.saveError(fmt.Errorf("php_serialize: Error while reading bool value: %v", err)) + } + + self.expect(SEPARATOR_VALUES) + return raw == '1' +} + +func (self *UnSerializer) decodeNumber(isFloat bool) PhpValue { + var ( + raw string + err error + val PhpValue + ) + self.expect(SEPARATOR_VALUE_TYPE) + + if raw, err = self.readUntil(SEPARATOR_VALUES); err != nil { + self.saveError(fmt.Errorf("php_serialize: Error while reading number value: %v", err)) + } else { + if isFloat { + if val, err = strconv.ParseFloat(raw, 64); err != nil { + self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to float: %v", raw, err)) + } + } else { + if val, err = strconv.Atoi(raw); err != nil { + self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to int: %v", raw, err)) + } + } + } + + return val +} + +func (self *UnSerializer) decodeString(left, right rune, isFinal bool) PhpValue { + var ( + err error + val PhpValue + strLen int + readLen int + ) + + strLen = self.readLen() + self.expect(left) + + if strLen > 0 { + buf := make([]byte, strLen) + if readLen, err = self.r.Read(buf); err != nil { + self.saveError(fmt.Errorf("php_serialize: Error while reading string value: %v", err)) + } else { + if readLen != strLen { + self.saveError(fmt.Errorf("php_serialize: Unable to read string. Expected %d but have got %d bytes", strLen, readLen)) + } else { + val = string(buf) + } + } + } + + self.expect(right) + if isFinal { + self.expect(SEPARATOR_VALUES) + } + return val +} + +func (self *UnSerializer) decodeArray() PhpValue { + var arrLen int + val := make(PhpArray) + + arrLen = self.readLen() + self.expect(DELIMITER_OBJECT_LEFT) + + for i := 0; i < arrLen; i++ { + k, errKey := self.Decode() + v, errVal := self.Decode() + + if errKey == nil && errVal == nil { + val[k] = v + /*switch t := k.(type) { + default: + self.saveError(fmt.Errorf("php_serialize: Unexpected key type %T", t)) + case string: + stringKey, _ := k.(string) + val[stringKey] = v + case int: + intKey, _ := k.(int) + val[strconv.Itoa(intKey)] = v + }*/ + } else { + self.saveError(fmt.Errorf("php_serialize: Error while reading key or(and) value of array")) + } + } + + self.expect(DELIMITER_OBJECT_RIGHT) + return val +} + +func (self *UnSerializer) decodeObject() PhpValue { + val := &PhpObject{ + className: self.readClassName(), + } + + rawMembers := self.decodeArray() + val.members, _ = rawMembers.(PhpArray) + + return val +} + +func (self *UnSerializer) decodeSerialized() PhpValue { + val := &PhpObjectSerialized{ + className: self.readClassName(), + } + + rawData := self.decodeString(DELIMITER_OBJECT_LEFT, DELIMITER_OBJECT_RIGHT, false) + val.data, _ = rawData.(string) + + if self.decodeFunc != nil && val.data != "" { + var err error + if val.value, err = self.decodeFunc(val.data); err != nil { + self.saveError(err) + } + } + + return val +} + +func (self *UnSerializer) decodeReference() PhpValue { + self.expect(SEPARATOR_VALUE_TYPE) + if _, err := self.readUntil(SEPARATOR_VALUES); err != nil { + self.saveError(fmt.Errorf("php_serialize: Error while reading reference value: %v", err)) + } + return nil +} + +func (self *UnSerializer) expect(expected rune) { + if token, _, err := self.r.ReadRune(); err != nil { + self.saveError(fmt.Errorf("php_serialize: Error while reading expected rune %#U: %v", expected, err)) + } else if token != expected { + if debugMode { + log.Printf("php_serialize: source\n%s\n", self.source) + log.Printf("php_serialize: reader info\n%#v\n", self.r) + } + self.saveError(fmt.Errorf("php_serialize: Expected %#U but have got %#U", expected, token)) + } +} + +func (self *UnSerializer) readUntil(stop rune) (string, error) { + var ( + token rune + err error + ) + buf := bytes.NewBuffer([]byte{}) + + for { + if token, _, err = self.r.ReadRune(); err != nil || token == stop { + break + } else { + buf.WriteRune(token) + } + } + + return buf.String(), err +} + +func (self *UnSerializer) readLen() int { + var ( + raw string + err error + val int + ) + self.expect(SEPARATOR_VALUE_TYPE) + + if raw, err = self.readUntil(SEPARATOR_VALUE_TYPE); err != nil { + self.saveError(fmt.Errorf("php_serialize: Error while reading lenght of value: %v", err)) + } else { + if val, err = strconv.Atoi(raw); err != nil { + self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to int: %v", raw, err)) + } else if val > UNSERIALIZABLE_OBJECT_MAX_LEN { + self.saveError(fmt.Errorf("php_serialize: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val)) + val = 0 + } + } + return val +} + +func (self *UnSerializer) readClassName() (res string) { + rawClass := self.decodeString(DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, false) + res, _ = rawClass.(string) + return +} + +func (self *UnSerializer) saveError(err error) { + if self.lastErr == nil { + self.lastErr = err + } +} + +func (self *UnSerializer) decodeSplArray() PhpValue { + var err error + val := &PhpSplArray{} + + self.expect(SEPARATOR_VALUE_TYPE) + self.expect(TOKEN_INT) + + flags := self.decodeNumber(false) + if flags == nil { + self.saveError(fmt.Errorf("php_serialize: Unable to read flags of SplArray")) + return nil + } + val.flags = PhpValueInt(flags) + + if val.array, err = self.Decode(); err != nil { + self.saveError(fmt.Errorf("php_serialize: Can't parse SplArray: %v", err)) + return nil + } + + self.expect(SEPARATOR_VALUES) + self.expect(TOKEN_SPL_ARRAY_MEMBERS) + self.expect(SEPARATOR_VALUE_TYPE) + + if val.properties, err = self.Decode(); err != nil { + self.saveError(fmt.Errorf("php_serialize: Can't parse properties of SplArray: %v", err)) + return nil + } + + return val +} diff --git a/point/point.go b/point/point.go new file mode 100644 index 0000000..f62c176 --- /dev/null +++ b/point/point.go @@ -0,0 +1,26 @@ +package point + +func PointLevel(point int64) int32 { + if point <= 50 { + return 1 + } + if point > 50 && point <= 200 { + return 2 + } + if point > 200 && point <= 500 { + return 3 + } + if point > 500 && point <= 1000 { + return 4 + } + if point > 1000 && point <= 2500 { + return 5 + } + if point > 2500 && point <= 5000 { + return 6 + } + if point > 5000 && point <= 8000 { + return 7 + } + return 8 +} diff --git a/transurl/transurl.go b/transurl/transurl.go new file mode 100644 index 0000000..85f00a0 --- /dev/null +++ b/transurl/transurl.go @@ -0,0 +1,48 @@ +package transurl + +var urlMap = map[string]string{ + "/beijing": "/question-list/alatest/1", + "/shanghai": "/question-list/alatest/2", + "/guangzhou": "/question-list/alatest/54", + "/shenzhen": "/question-list/alatest/55", + "/chengdu": "/question-list/alatest/63", + "/xi'an": "/question-list/alatest/64", + "/hangzhou": "/question-list/alatest/59", + "/qingdao": "/question-list/alatest/65", + "/dalian": "/question-list/alatest/69", + "/suzhou": "/question-list/alatest/67", + "/nanjing": "/question-list/alatest/68", + "/arts-entertainment": "/question-list/alatest/34", + "/business-jobs": "/question-list/alatest/3", + "/culture": "/question-list/alatest/4", + "/falimy-kids": "/question-list/alatest/45", + "/food": "/question-list/alatest/35", + "/general": "/question-list/alatest/36", + "/health-safety": "/question-list/alatest/39", + "/housing": "/question-list/alatest/49", + "/lifestyle": "/question-list/alatest/1058", + "/money-banking": "/question-list/alatest/1059", + "/relationships": "/question-list/alatest/53", + "/shopping": "/question-list/alatest/41", + "/sports": "/question-list/alatest/95", + "/teaching-learning": "/question-list/alatest/43", + "/transport-travel": "/question-list/alatest/47", + "/visa-legalities": "/question-list/alatest/48", + "/web-technology": "/question-list/alatest/44", +} + +func GetLonUrl(src string) string { + if v, ok := urlMap[src]; ok { + return v + } + return src +} + +func GetShortUrl(dst string) string { + for src, dt := range urlMap { + if dt == dst { + return src + } + } + return dst +} diff --git a/xarray/xarray.go b/xarray/xarray.go new file mode 100644 index 0000000..5e3a58b --- /dev/null +++ b/xarray/xarray.go @@ -0,0 +1,53 @@ +package xarray + +import ( + "reflect" + "strings" +) + +func ToSlice(slice interface{}) []interface{} { + s := reflect.ValueOf(slice) + if s.Kind() != reflect.Slice { + panic("InterfaceSlice() given a non-slice type") + } + + ret := make([]interface{}, s.Len()) + + for i := 0; i < s.Len(); i++ { + ret[i] = s.Index(i).Interface() + } + + return ret +} + +func ToCountMap(strArr []string) map[string]int64 { + mps := make(map[string]int64) + for _, v := range strArr { + mps[strings.ToLower(v)]++ + } + return mps +} + +func InArray(need interface{}, list []interface{}) bool { + for _, v := range list { + if need == v { + return true + } + } + return false +} +func UniqArray(list []int64) []int64 { + result := make([]int64, 0) + if len(list) == 0 { + return result + } + mp := make(map[int64]bool) + for _, v := range list { + if mp[v] { + continue + } + result = append(result, v) + mp[v] = true + } + return result +} diff --git a/xhttp/xhttp.go b/xhttp/xhttp.go new file mode 100644 index 0000000..15d27d0 --- /dev/null +++ b/xhttp/xhttp.go @@ -0,0 +1,189 @@ +package xhttp + +import ( + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" +) + +const timeout = time.Second * 120 + +func Request(method string, reqUrl string, postData string, header http.Header) (string, error) { + var res string + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{ + Transport: transport, + Timeout: timeout, + } + var ioreader io.Reader + if postData != "" { + ioreader = strings.NewReader(postData) + } + req, err := http.NewRequest(method, reqUrl, ioreader) + if err != nil { + return res, err + } + if header != nil { + req.Header = header + } + + resp, err := client.Do(req) + if err != nil { + return res, err + } + defer resp.Body.Close() + + if resp.StatusCode == 200 { + byt, err := ioutil.ReadAll(resp.Body) + if err != nil { + return res, err + } + return string(byt), nil + } else { + return res, fmt.Errorf("code:%d message:%s", resp.StatusCode, resp.Status) + } +} +func Get(reqUrl string, timeout time.Duration) (string, error) { + var res string + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{ + Transport: transport, + Timeout: timeout, + } + req, err := http.NewRequest("GET", reqUrl, nil) + if err != nil { + return res, err + } + resp, err := client.Do(req) + if err != nil { + return res, err + } + defer resp.Body.Close() + + if resp.StatusCode == 200 { + byt, err := ioutil.ReadAll(resp.Body) + if err != nil { + return res, err + } + return string(byt), nil + } else { + return res, fmt.Errorf("code:%d message:%s", resp.StatusCode, resp.Status) + } +} +func fetcher(method, u, post string, headers map[string]string, timeout int64) (*[]byte, error) { + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{ + Transport: transport, + Timeout: time.Second * time.Duration(timeout), + } + req, err := http.NewRequest(method, u, strings.NewReader(post)) + if err != nil { + return nil, err + } + for k, v := range headers { + req.Header.Add(k, v) + } + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("FetchUrl status:%s", resp.Status) + } + return &body, err +} + +func PostForm(u, post string, headers map[string]string) (*[]byte, error) { + if headers == nil { + headers = make(map[string]string) + } + headers["Content-Type"] = "application/x-www-form-urlencoded" + return fetcher("POST", u, post, headers, 60) +} + +func PostJson(u, post string, headers map[string]string) (*[]byte, error) { + if headers == nil { + headers = make(map[string]string) + } + + headers["Content-Type"] = "application/json;charset=UTF-8" + fmt.Printf("%+v\n", post) + fmt.Printf("%+v\n", headers) + return fetcher("POST", u, post, headers, 60) +} + +func GetWithHeader(u string, headers map[string]string) (*[]byte, error) { + if headers == nil { + headers = make(map[string]string) + } + return fetcher("GET", u, "", headers, 60) +} + +type apiRes struct { + Status int64 `json:"status"` +} + +func SenEmail(u string, data map[string]string) error { + fmt.Println(u) + postByte, _ := json.Marshal(data) + fmt.Printf("%s\n", postByte) + values := url.Values{} + for k, v := range data { + values.Add(k, v) + } + fmt.Println(data) + res, err := PostForm(u, values.Encode(), nil) + if err != nil { + return err + } + fmt.Printf("%s\n", *res) + result := apiRes{} + if err := json.Unmarshal(*res, &result); err != nil { + return err + } + if result.Status != 200 { + return errors.New(string(*res)) + } + return nil +} + +type shotResp struct { + Code int64 `json:"code"` + Info string `json:"info"` + Message string `json:"message"` +} + +func ShotUrl(u string) (string, error) { + values := url.Values{} + values.Add("long_url", u) + res, err := PostForm(`https://echinajobs.cn/add`, values.Encode(), nil) + if err != nil { + return "", err + } + result := shotResp{} + if err := json.Unmarshal(*res, &result); err != nil { + return "", err + } + if result.Code != 200 { + return "", errors.New(string(*res)) + } + return result.Info, nil +} diff --git a/xip/xip.go b/xip/xip.go new file mode 100644 index 0000000..c538628 --- /dev/null +++ b/xip/xip.go @@ -0,0 +1,13 @@ +package xip + +import ( + "context" + "github.com/gin-gonic/gin" +) + +func GetIpFromContext(ctx context.Context) string { + if v, ok := ctx.(*gin.Context); ok { + return v.ClientIP() + } + return "" +} diff --git a/xjson/xjson.go b/xjson/xjson.go new file mode 100644 index 0000000..962a2c9 --- /dev/null +++ b/xjson/xjson.go @@ -0,0 +1,11 @@ +package xjson + +import "encoding/json" + +func MustMarshal(data any) string { + b, err := json.Marshal(data) + if err != nil { + return "" + } + return string(b) +} diff --git a/xmath/math.go b/xmath/math.go new file mode 100644 index 0000000..20eb547 --- /dev/null +++ b/xmath/math.go @@ -0,0 +1,77 @@ +package xmath + +import ( + "fmt" + //"github.com/shopspring/decimal" + "math" + "math/rand" + "strconv" + "strings" +) + +// 小数点后 n 位 - 四舍五入 +func RoundedFixed(val float64, n int) float64 { + shift := math.Pow(10, float64(n)) + fv := 0.0000000001 + val //对浮点数产生.xxx999999999 计算不准进行处理 + return math.Floor(fv*shift+.5) / shift +} + +// 小数点后 n 位 - 舍去 +func TruncRound(val float64, n int) float64 { + floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", val) + temp := strings.Split(floatStr, ".") + var newFloat string + if len(temp) < 2 || n >= len(temp[1]) { + newFloat = floatStr + } else { + newFloat = temp[0] + "." + temp[1][:n] + } + inst, _ := strconv.ParseFloat(newFloat, 64) + return inst +} + +// RandInt64 随机范围取值 +func RandInt64(min, max int64) int64 { + if min >= max || min == 0 || max == 0 { + return max + } + return rand.Int63n(max-min) + min +} +func FloatWithoutRightDot(n float64) string { + s := fmt.Sprintf("%.2f", n) + ss := strings.Split(s, ".") + ss1 := strings.TrimRight(ss[1], "0") + if ss1 == "" { + return ss[0] + } else { + return fmt.Sprintf("%s.%s", ss[0], ss1) + } +} + +func NumberFormat(str string) string { + length := len(str) + if length < 4 { + return str + } + arr := strings.Split(str, ".") //用小数点符号分割字符串,为数组接收 + length1 := len(arr[0]) + if length1 < 4 { + return str + } + count := (length1 - 1) / 3 + for i := 0; i < count; i++ { + arr[0] = arr[0][:length1-(i+1)*3] + "," + arr[0][length1-(i+1)*3:] + } + return strings.Join(arr, ".") //将一系列字符串连接为一个字符串,之间用sep来分隔。 +} + +func ConvertInt64(s string) int64 { + v, _ := strconv.ParseInt(s, 10, 64) + return v +} + +// +//func Round(f float64, n int32) float64 { +// v, _ := decimal.NewFromFloat(f).Round(n).Float64() +// return v +//} diff --git a/xmd5/md5.go b/xmd5/md5.go new file mode 100644 index 0000000..22026fb --- /dev/null +++ b/xmd5/md5.go @@ -0,0 +1,12 @@ +package xmd5 + +import ( + "crypto/md5" + "encoding/hex" +) + +func MD5(str string) string { + s := md5.New() + s.Write([]byte(str)) + return hex.EncodeToString(s.Sum(nil)) +} diff --git a/xpage/xpage.go b/xpage/xpage.go new file mode 100644 index 0000000..0c3cab2 --- /dev/null +++ b/xpage/xpage.go @@ -0,0 +1,8 @@ +package xpage + +func GetPageOffset(page int32, size int32) int { + if page < 1 { + page = 1 + } + return int((page - 1) * size) +} diff --git a/xredis/xredis.go b/xredis/xredis.go new file mode 100644 index 0000000..dceb8ca --- /dev/null +++ b/xredis/xredis.go @@ -0,0 +1,59 @@ +package xredis + +// +//import ( +// "context" +// "encoding/json" +// "fmt" +// "github.com/go-redis/redis/v8" +// "github.com/pkg/errors" +// "time" +//) +// +//var redisClient *redis.Client +// +//func Init(host, password string, db int) error { +// redisClient = redis.NewClient(&redis.Options{ +// Network: "tcp", +// Addr: host, +// Password: password, +// DB: db, +// }) +// return redisClient.Ping(context.Background()).Err() +//} +// +//func Client() *redis.Client { +// return redisClient +//} +// +//func GetData[T any](ctx context.Context, key string, fn func() (T, error), dur time.Duration) (T, error) { +// var result T +// res := redisClient.Get(ctx, key).Val() +// if res != "" { +// err := json.Unmarshal([]byte(res), &result) +// if err == nil { +// return result, nil +// } +// } +// r, err := fn() +// if err != nil { +// return result, err +// } +// if b, err := json.Marshal(r); err == nil { +// redisClient.Set(ctx, key, string(b), dur) +// } +// return r, nil +//} +//func Lock[T any](ctx context.Context, key string, fn func() (T, error)) (T, error) { +// var res T +// lockKey := fmt.Sprintf("lock:%s", key) +// ok, err := redisClient.SetNX(ctx, lockKey, 1, time.Second*20).Result() +// if err != nil { +// return res, errors.Wrap(err, "") +// } +// if !ok { +// return res, errors.Wrap(errors.New(fmt.Sprintf("lock %s exists", lockKey)), "") +// } +// defer redisClient.Del(ctx, lockKey) +// return fn() +//} diff --git a/xredis/xredis_test.go b/xredis/xredis_test.go new file mode 100644 index 0000000..d93be3c --- /dev/null +++ b/xredis/xredis_test.go @@ -0,0 +1,28 @@ +package xredis + +import ( + "context" + "fmt" + "testing" + "time" +) + +type Res struct { + Name string +} + +func TestGetData(t *testing.T) { + _ = Init("localhost:6379", "", 1) + res, err := GetData(context.Background(), "userId1212", func() (*Res, error) { + fmt.Println("from db") + in := &Res{ + Name: "ttt", + } + return in, nil + }, time.Second*50) + + if err != nil { + t.Error(err) + } + fmt.Printf("%+v\n", res) +} diff --git a/xstring/stringx.go b/xstring/stringx.go new file mode 100644 index 0000000..172bc28 --- /dev/null +++ b/xstring/stringx.go @@ -0,0 +1,641 @@ +package xstring + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/PuerkitoBio/goquery" + "github.com/pkg/errors" + "reflect" + "regexp" + "sort" + "strconv" + "strings" +) + +func TableFieldNames(obj any) ([]string, string) { + v := reflect.TypeOf(obj) + cols := []string{} + primary := "" + for i := 0; i < v.NumField(); i++ { + cols = append(cols, v.Field(i).Tag.Get("db")) + if primary == "" { + primary = v.Field(i).Tag.Get("primaryKey") + } + } + return cols, primary +} + +func Remove[T string](cols []T, key T) []T { + arr := make([]T, 0) + for _, v := range cols { + if key != v { + arr = append(arr, v) + } + } + return arr +} + +func NamedSql(cls []string) string { + str := []string{} + for _, v := range cls { + str = append(str, fmt.Sprintf("%s=:%s", v, v)) + } + return strings.Join(str, ",") +} + +func StripTags(content string) string { + re := regexp.MustCompile(`<(.|\n)*?>`) + return re.ReplaceAllString(content, "") +} +func SubString(str string, begin, length int) string { + rs := []rune(str) + lth := len(rs) + if begin < 0 { + begin = 0 + } + if begin >= lth { + begin = lth + } + end := begin + length + + if end > lth { + end = lth + } + return string(rs[begin:end]) +} +func Nl2br(str string) string { + return strings.Join(strings.Split(strings.Join(strings.Split(str, "\r"), "
"), "\n"), "
") +} + +func Serialize(value interface{}) ([]byte, error) { + + if value == nil { + return MarshalNil(), nil + } + + t := reflect.TypeOf(value) + switch t.Kind() { + case reflect.Bool: + return MarshalBool(value.(bool)), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + return MarshalNumber(value), nil + case reflect.String: + return MarshalString(value.(string)), nil + case reflect.Map: + return MarshalMap(value) + case reflect.Slice: + return MarshalSlice(value) + default: + return nil, fmt.Errorf("Marshal: Unknown type %T with value %#v", t, value) + } +} + +func MarshalNil() []byte { + return []byte("N;") +} + +func MarshalBool(value bool) []byte { + if value { + return []byte("b:1;") + } + + return []byte("b:0;") +} + +func MarshalNumber(value interface{}) []byte { + var val string + + isFloat := false + + switch value.(type) { + default: + val = "0" + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + val, _ = NumericalToString(value) + case float32, float64: + val, _ = NumericalToString(value) + isFloat = true + } + + if isFloat { + return []byte("d:" + val + ";") + + } else { + return []byte("i:" + val + ";") + } +} +func NumericalValue(value reflect.Value) (float64, bool) { + switch value.Type().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(value.Int()), true + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return float64(value.Uint()), true + + case reflect.Float32, reflect.Float64: + return value.Float(), true + + default: + return 0, false + } +} +func MarshalString(value string) []byte { + return []byte(fmt.Sprintf("s:%d:\"%s\";", len(value), value)) +} +func LessValue(a, b reflect.Value) bool { + aValue, aNumerical := NumericalValue(a) + bValue, bNumerical := NumericalValue(b) + + if aNumerical && bNumerical { + return aValue < bValue + } + + if !aNumerical && !bNumerical { + // In theory this should mean they are both strings. In reality + // they could be any other type and the String() representation + // will be something like "" if it is not a string. Since + // distinct values of non-strings still return the same value + // here that's what makes the ordering undefined. + return strings.Compare(a.String(), b.String()) < 0 + } + + // Numerical values are always treated as less than other types + // (including strings that might represent numbers themselves). The + // inverse is also true. + return aNumerical && !bNumerical +} + +func MarshalMap(value interface{}) ([]byte, error) { + + s := reflect.ValueOf(value) + + mapKeys := s.MapKeys() + sort.Slice(mapKeys, func(i, j int) bool { + return LessValue(mapKeys[i], mapKeys[j]) + }) + + var buffer bytes.Buffer + for _, mapKey := range mapKeys { + m, err := Serialize(mapKey.Interface()) + if err != nil { + return nil, err + } + + buffer.Write(m) + + m, err = Serialize(s.MapIndex(mapKey).Interface()) + if err != nil { + return nil, err + } + + buffer.Write(m) + } + + return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil +} + +func MarshalSlice(value interface{}) ([]byte, error) { + s := reflect.ValueOf(value) + + var buffer bytes.Buffer + for i := 0; i < s.Len(); i++ { + m, err := Serialize(i) + if err != nil { + return nil, err + } + + buffer.Write(m) + + m, err = Serialize(s.Index(i).Interface()) + if err != nil { + return nil, err + } + + buffer.Write(m) + } + + return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil +} + +const UNSERIALIZABLE_OBJECT_MAX_LEN = int64(10 * 1024 * 1024 * 1024) + +func UnSerialize(data []byte) (interface{}, error) { + reader := bytes.NewReader(data) + return unMarshalByReader(reader) +} + +func UnSerializeBind(data []byte, post any) error { + reader := bytes.NewReader(data) + b, err := unMarshalByReader(reader) + if err != nil { + return errors.Wrap(err, "") + } + bb, err := json.Marshal(b) + if err != nil { + return errors.Wrap(err, "") + } + err = json.Unmarshal(bb, post) + return errors.Wrap(err, "") +} + +func unMarshalByReader(reader *bytes.Reader) (interface{}, error) { + + for { + + if token, _, err := reader.ReadRune(); err == nil { + switch token { + default: + return nil, fmt.Errorf("UnMarshal: Unknown token %#U", token) + case 'N': + return unMarshalNil(reader) + case 'b': + return unMarshalBool(reader) + case 'i': + return unMarshalNumber(reader, false) + case 'd': + return unMarshalNumber(reader, true) + case 's': + return unMarshalString(reader, true) + case 'a': + return unMarshalArray(reader) + // case 'O': + + // case 'C': + + // case 'R', 'r': + + // case 'x': + + } + } + } + +} + +func unMarshalNil(reader *bytes.Reader) (interface{}, error) { + _ = expect(reader, ';') + + return nil, nil +} + +func unMarshalBool(reader *bytes.Reader) (interface{}, error) { + var ( + raw rune + err error + ) + err = expect(reader, ':') + if err != nil { + return nil, err + } + + if raw, _, err = reader.ReadRune(); err != nil { + return nil, fmt.Errorf("UnMarshal: Error while reading bool value: %v", err) + } + + err = expect(reader, ';') + if err != nil { + return nil, err + } + return raw == '1', nil +} + +func unMarshalNumber(reader *bytes.Reader, isFloat bool) (interface{}, error) { + var ( + raw string + err error + val interface{} + ) + err = expect(reader, ':') + if err != nil { + return nil, err + } + + if raw, err = readUntil(reader, ';'); err != nil { + return nil, fmt.Errorf("UnMarshal: Error while reading number value: %v", err) + } else { + if isFloat { + if val, err = strconv.ParseFloat(raw, 64); err != nil { + return nil, fmt.Errorf("UnMarshal: Unable to convert %s to float: %v", raw, err) + } + } else { + if val, err = strconv.Atoi(raw); err != nil { + return nil, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err) + } + } + } + + return val, nil +} + +func unMarshalString(reader *bytes.Reader, isFinal bool) (interface{}, error) { + var ( + err error + val interface{} + strLen int + readLen int + ) + + strLen, err = readLength(reader) + if err != nil { + return nil, err + } + err = expect(reader, '"') + if err != nil { + return nil, err + } + + if strLen > 0 { + buf := make([]byte, strLen) + if readLen, err = reader.Read(buf); err != nil { + return nil, fmt.Errorf("UnMarshal: Error while reading string value: %v", err) + } else { + if readLen != strLen { + return nil, fmt.Errorf("UnMarshal: Unable to read string. Expected %d but have got %d bytes", strLen, readLen) + } else { + val = string(buf) + } + } + } + + err = expect(reader, '"') + if err != nil { + return nil, err + } + if isFinal { + err = expect(reader, ';') + if err != nil { + return nil, err + } + } + return val, nil +} + +func unMarshalArray(reader *bytes.Reader) (interface{}, error) { + var arrLen int + var err error + val := make(map[string]interface{}) + + arrLen, err = readLength(reader) + + if err != nil { + return nil, err + } + err = expect(reader, '{') + if err != nil { + return nil, err + } + indexLen := 0 + for i := 0; i < arrLen; i++ { + k, err := unMarshalByReader(reader) + if err != nil { + return nil, err + } + v, err := unMarshalByReader(reader) + if err != nil { + return nil, err + } + + // if errKey == nil && errVal == nil { + // val[k] = v + switch t := k.(type) { + default: + return nil, fmt.Errorf("UnMarshal: Unexpected key type %T", t) + case string: + stringKey, _ := k.(string) + val[stringKey] = v + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: + // intKey, _ := k.(int) + // val[strconv.Itoa(intKey)] = v + stringKey, _ := NumericalToString(k) + val[stringKey] = v + + // stringI, _ := utils.NumericalToString(i) + if i == k { + indexLen++ + } + + } + // } else { + // return nil, fmt.Errorf("UnMarshal: Error while reading key or(and) value of array") + // } + } + + err = expect(reader, '}') + if err != nil { + return nil, err + } + + if indexLen == arrLen { + var slice []interface{} + for _, row := range val { + slice = append(slice, row) + } + return slice, nil + } + + return val, nil +} + +func expect(reader *bytes.Reader, expected rune) error { + if token, _, err := reader.ReadRune(); err != nil { + return fmt.Errorf("UnMarshal: Error while reading expected rune %#U: %v", expected, err) + } else if token != expected { + return fmt.Errorf("UnMarshal: Expected %#U but have got %#U", expected, token) + } + return nil +} + +func readUntil(reader *bytes.Reader, stop rune) (string, error) { + var ( + token rune + err error + ) + buf := bytes.NewBuffer([]byte{}) + + for { + if token, _, err = reader.ReadRune(); err != nil || token == stop { + break + } else { + buf.WriteRune(token) + } + } + + return buf.String(), err +} + +func readLength(reader *bytes.Reader) (int, error) { + var ( + raw string + err error + val int + ) + err = expect(reader, ':') + if err != nil { + return 0, err + } + + if raw, err = readUntil(reader, ':'); err != nil { + return 0, fmt.Errorf("UnMarshal: Error while reading lenght of value: %v", err) + } else { + if val, err = strconv.Atoi(raw); err != nil { + return 0, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err) + } else if int64(val) > UNSERIALIZABLE_OBJECT_MAX_LEN { + return 0, fmt.Errorf("UnMarshal: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val) + } + } + return val, nil +} + +func NumericalToString(value interface{}) (string, bool) { + var val string + + switch intVal := value.(type) { + default: + return "0", false + case int: + val = strconv.FormatInt(int64(intVal), 10) + case int8: + val = strconv.FormatInt(int64(intVal), 10) + case int16: + val = strconv.FormatInt(int64(intVal), 10) + case int32: + val = strconv.FormatInt(int64(intVal), 10) + case int64: + val = strconv.FormatInt(int64(intVal), 10) + case uint: + val = strconv.FormatUint(uint64(intVal), 10) + case uint8: + val = strconv.FormatUint(uint64(intVal), 10) + case uint16: + val = strconv.FormatUint(uint64(intVal), 10) + case uint32: + val = strconv.FormatUint(uint64(intVal), 10) + case uint64: + val = strconv.FormatUint(intVal, 10) + case float32: + val = strconv.FormatFloat(float64(intVal), 'f', -1, 32) + case float64: + val = strconv.FormatFloat(intVal, 'f', -1, 64) + } + return val, true +} + +//func StrToArr(str string) []string { +// re := regexp.MustCompile(`<(.|\n)*?>`) +// str = re.ReplaceAllString(str, " ") +// str = strings.ReplaceAll(str, `"`, "") +// str = strings.ReplaceAll(str, `'`, "") +// str = strings.ReplaceAll(str, "`", "") +// str = strings.ReplaceAll(str, ",", "") +// str = strings.ReplaceAll(str, " ", "") +// newStr := []string{} +// tmp := "" +// for i := 0; i < len(str); { +// r, n := utf8.DecodeRuneInString(str[i:]) +// if n == 3 { +// tmp = "" +// newStr = append(newStr, string(r)) +// } else { +// +// if string(r) == " " { +// newStr = append(newStr, tmp) +// tmp = "" +// } else if string(r) != "\n" { +// tmp = fmt.Sprintf("%s%c", tmp, r) +// } +// } +// i += n +// } +// if tmp != "" { +// newStr = append(newStr, tmp) +// tmp = "" +// } +// newStr1 := make([]string, 0) +// for _, v := range newStr { +// if strings.TrimSpace(v) == "" { +// continue +// } +// newStr1 = append(newStr1, strings.TrimSpace(v)) +// } +// return newStr1 +//} + +//func StrToUrl(str string) string { +// strs := []string{} +// for _, v := range StrToArr(str) { +// fmt.Println(v) +// } +// +// return strings.Join(strs, "-") +//} + +func Base64Encode(s string) string { + return base64.StdEncoding.EncodeToString([]byte(s)) +} + +func Base64Decode(s string) (string, error) { + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return "", errors.Wrap(err, "") + } + return string(b), nil +} + +func HtmlToText(body string) ([]string, error) { + body = strings.ReplaceAll(body, " ", " ") + ss := "<(.|\\n)*?>" + body = regexp.MustCompile(ss).ReplaceAllString(body, " ") + doc, err := goquery.NewDocumentFromReader(strings.NewReader(body)) + if err != nil { + return nil, errors.Wrap(err, "") + } + defer doc.Clone() + v := doc.Text() + var hzRegexp = regexp.MustCompile("^[a-zA-Z0-9\u4e00-\u9fa5]$") + var zwReg = regexp.MustCompile("^[\u4e00-\u9fa5]$") + strn := "" + ars := []string{} + for _, c := range v { + filter := false + if hzRegexp.MatchString(string(c)) { + if zwReg.MatchString(string(c)) { + strn += string(c) + filter = true + } else { + strn += string(c) + } + } else { + filter = true + } + fmt.Println(strn) + if filter && strn != "" { + ars = append(ars, strn) + strn = "" + } + } + if strn != "" { + ars = append(ars, strn) + } + return ars, nil +} +func ContentText(body string) string { + doc, err := goquery.NewDocumentFromReader(strings.NewReader(body)) + if err != nil { + return err.Error() + } + defer doc.Clone() + return doc.Text() +} +func GetPicture(pic string) string { + if pic == "" { + return "/sites/default/files/imagecache/ic50x50/avatar_selection/o2QQW.jpg.png" + } + if !strings.HasPrefix(pic, "/") { + return "/" + pic + } + return pic +} diff --git a/xtime/xtime.go b/xtime/xtime.go new file mode 100644 index 0000000..071aec0 --- /dev/null +++ b/xtime/xtime.go @@ -0,0 +1,69 @@ +package xtime + +import ( + "fmt" + "strings" + "time" +) + +const DefaultTimeLayout = "2006-01-02 15:04:05" + +func LeaTime(t int64) string { + leaSeconds := time.Now().Unix() - t + mps := []struct { + Seconds int64 + Title string + }{ + {31536000, "years"}, + {604800, "weeks"}, + {86400, "days"}, + {3600, "hours"}, + {60, "min"}, + {1, "sec"}, + } + res := []string{} + for _, v := range mps { + if leaSeconds > v.Seconds { + if len(res) == 2 { + break + } + n := leaSeconds / v.Seconds + tt := v.Title + if n == 1 { + tt = strings.TrimRight(v.Title, "s") + } + res = append(res, fmt.Sprintf("%d %s", n, tt)) + leaSeconds -= v.Seconds * n + } + } + return strings.Join(res, " ") +} + +func UnixToTime(unix int64) time.Time { + return time.Unix(unix, 0) +} +func ParseTime(day string, layout string) int64 { + if day == "" { + return 0 + } + t, err := time.ParseInLocation(layout, day, time.Local) + if err != nil { + fmt.Println(err) + return 0 + } + return t.Unix() +} + +const DefaultLayout = "2006-01-02 15:04:05" + +func TimeTodayMinAndMax(ctime time.Time) (min, max time.Time) { + start, _ := time.ParseInLocation(DefaultLayout, ctime.Format("2006-01-02")+" 00:00:00", time.Local) + end, _ := time.ParseInLocation(DefaultLayout, ctime.Format("2006-01-02")+" 23:59:59", time.Local) + return start, end +} +func UnixToStr(t int64) string { + if t == 0 { + return "" + } + return UnixToTime(t).Format(DefaultTimeLayout) +} diff --git a/xtime/xtime_test.go b/xtime/xtime_test.go new file mode 100644 index 0000000..9f4049a --- /dev/null +++ b/xtime/xtime_test.go @@ -0,0 +1,10 @@ +package xtime + +import ( + "fmt" + "testing" +) + +func TestLeaTime(t *testing.T) { + fmt.Println(LeaTime(1651812894)) +} diff --git a/xuser/xuser.go b/xuser/xuser.go new file mode 100644 index 0000000..1a2bdb2 --- /dev/null +++ b/xuser/xuser.go @@ -0,0 +1,23 @@ +package xuser + +import ( + "github.com/gin-gonic/gin" +) + +type requestUserIdKey struct { +} + +func GetUserNameFromContext(ctx *gin.Context) string { + v, ok := ctx.Get("userId") + if !ok { + return "" + } + //if value, ok := ctx.Value(requestUserIdKey{}).(string); ok { + // return value + //} + return v.(string) +} +func SetContextUserId(ctx *gin.Context, username string) { + ctx.Set("userId", username) + //return context.WithValue(ctx, requestUserIdKey{}, username) +}