From cef20c907eb1463a6a83bc52748d250d16062484 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Thu, 20 Mar 2025 00:40:42 +0100 Subject: [PATCH 1/9] Add module files --- go.mod | 49 +++++++++++++++++++++++++ go.sum | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..72c1777 --- /dev/null +++ b/go.mod @@ -0,0 +1,49 @@ +module github.com/cthit/gotify + +go 1.23.0 + +toolchain go1.23.7 + +require ( + github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b + github.com/spf13/viper v1.20.0 + golang.org/x/net v0.37.0 + golang.org/x/oauth2 v0.28.0 + google.golang.org/api v0.227.0 +) + +require ( + cloud.google.com/go/auth v0.15.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/grpc v1.71.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0038de1 --- /dev/null +++ b/go.sum @@ -0,0 +1,110 @@ +cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= +cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b h1:g2Qcs0B+vOQE1L3a7WQ/JUUSzJnHbTz14qkJSqEWcF4= +github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b/go.mod h1:Ag7UMbZNGrnHwaXPJOUKJIVgx4QOWMOWZngrvsN6qak= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= +github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc= +google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 51b92b2ac7df839ef5388dc4a00e24a71f9a4b31 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Thu, 20 Mar 2025 00:41:29 +0100 Subject: [PATCH 2/9] Add support for attachments, UTF-8 subjects --- google_mail/google_service.go | 40 ++++++++++++++++++++++++++--------- mail.go | 15 +++++++++---- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/google_mail/google_service.go b/google_mail/google_service.go index 7b07737..5e477bb 100644 --- a/google_mail/google_service.go +++ b/google_mail/google_service.go @@ -7,7 +7,7 @@ import ( "golang.org/x/oauth2/google" "encoding/base64" - "io/ioutil" + "os" "github.com/cthit/gotify" ) @@ -20,7 +20,7 @@ type googleService struct { func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) (func() gotify.MailService, error) { - jsonKey, err := ioutil.ReadFile(keyPath) + jsonKey, err := os.ReadFile(keyPath) if err != nil { return nil, err } @@ -47,9 +47,6 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) ( adminMail: adminMail, debug: debug, } - if err != nil { - return nil, err - } return func() gotify.MailService { return gs @@ -59,11 +56,34 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) ( func (g *googleService) SendMail(mail gotify.Mail) (gotify.Mail, error) { mail.From = g.adminMail - - msgRaw := "From: " + mail.From + "\r\n" + - "To: " + mail.To + "\r\n" + - "Subject: " + mail.Subject + "\r\n\r\n" + - mail.Body + "\r\n" + var msgRaw string + subject := "=?UTF-8?B?" + base64.StdEncoding.EncodeToString([]byte(mail.Subject)) + "?=" + if len(mail.Attachments) > 0 { + boundary := "my-boundary-779" + msgRaw = "From: " + mail.From + "\r\n" + + "To: " + mail.To + "\r\n" + + "Subject: " + subject + "\r\n" + + "MIME-Version: 1.0\r\n" + + "Content-Type: multipart/mixed; boundary=" + boundary + "\r\n\r\n" + + "--" + boundary + "\r\n" + + "Content-Type: text/plain; charset=UTF-8\r\n\r\n" + + mail.Body + "\r\n" + + for _, attachment := range mail.Attachments { + msgRaw += "--" + boundary + "\r\n" + + "Content-Type: " + attachment.ContentType + "; name=\"" + attachment.Name + "\"\r\n" + + "Content-Disposition: attachment; filename=\"" + attachment.Name + "\"\r\n" + + "Content-Transfer-Encoding: base64\r\n\r\n" + + attachment.Data + "\r\n" + } + + msgRaw += "--" + boundary + "--" + } else { + msgRaw = "From: " + mail.From + "\r\n" + + "To: " + mail.To + "\r\n" + + "Subject: " + subject + "\r\n\r\n" + + mail.Body + "\r\n" + } msg := &gmail.Message{ Raw: base64.RawURLEncoding.EncodeToString([]byte(msgRaw)), diff --git a/mail.go b/mail.go index 7cf0d81..3720d02 100644 --- a/mail.go +++ b/mail.go @@ -1,8 +1,15 @@ package gotify type Mail struct { - To string `json:"to"` - From string `json:"from"` - Subject string `json:"subject"` - Body string `json:"body"` + To string `json:"to"` + From string `json:"from"` + Subject string `json:"subject"` + Body string `json:"body"` + Attachments []Attachment `json:"attachments"` +} + +type Attachment struct { + Name string `json:"name"` + Data string `json:"data"` + ContentType string `json:"content_type"` } From e67160a30f681a97d977fe4dafcb1b19a0825b58 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Thu, 20 Mar 2025 00:43:17 +0100 Subject: [PATCH 3/9] Add workflow --- .github/workflows/docker.yml | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..ce7ac4b --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,50 @@ +name: Docker + +on: + push: + branches: [main] + tags: + - v[0-9]+.[0-9]+.[0-9]+* + pull_request: + branches: [main] + +jobs: + build: + name: Build image + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository_owner }}/gotify + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + type=edge + type=ref,event=pr + + - name: Build Docker image (and push on main) + uses: docker/build-push-action@v6 + with: + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From ef48fea8da471662a6e28785f21dcb3e48ea4f3b Mon Sep 17 00:00:00 2001 From: Goostaf Date: Sat, 22 Mar 2025 00:46:02 +0100 Subject: [PATCH 4/9] Improve Dockerfile --- Dockerfile | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d7833f..75a5db7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Dockerfile for gotify production -FROM golang:alpine AS buildStage -MAINTAINER digIT +FROM golang:alpine3.21 AS buildStage +LABEL maintainer="digIT " # Install git RUN apk update @@ -12,10 +12,7 @@ RUN mkdir -p $GOPATH/src/github.com/cthit/gotify COPY . $GOPATH/src/github.com/cthit/gotify WORKDIR $GOPATH/src/github.com/cthit/gotify/cmd -# Grab dependencies -RUN go get -d -v ./... - -# build binary +# Build binary RUN go install -v RUN mkdir /app && mv $GOPATH/bin/cmd /app/gotify @@ -23,7 +20,7 @@ RUN mkdir /app && mv $GOPATH/bin/cmd /app/gotify # PRODUCTION STAGE # ########################## FROM alpine -MAINTAINER digIT +LABEL maintainer="digIT " # Add standard certificates RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* From e1f13d39783ae74af507d92c3f2123ec9af5532c Mon Sep 17 00:00:00 2001 From: Goostaf Date: Sat, 22 Mar 2025 01:35:34 +0100 Subject: [PATCH 5/9] More random boundary, fix deprecated client --- google_mail/google_service.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/google_mail/google_service.go b/google_mail/google_service.go index 5e477bb..a05fb39 100644 --- a/google_mail/google_service.go +++ b/google_mail/google_service.go @@ -1,7 +1,10 @@ package google_mail import ( + "fmt" + "google.golang.org/api/gmail/v1" // Imports as gmail + "google.golang.org/api/option" "golang.org/x/net/context" "golang.org/x/oauth2/google" @@ -10,6 +13,8 @@ import ( "os" "github.com/cthit/gotify" + + "math/rand" ) type googleService struct { @@ -37,7 +42,7 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) ( // Create a http client client := config.Client(context.Background()) - mailService, err := gmail.New(client) + mailService, err := gmail.NewService(context.Background(), option.WithHTTPClient(client)) if err != nil { return nil, err } @@ -58,8 +63,10 @@ func (g *googleService) SendMail(mail gotify.Mail) (gotify.Mail, error) { mail.From = g.adminMail var msgRaw string subject := "=?UTF-8?B?" + base64.StdEncoding.EncodeToString([]byte(mail.Subject)) + "?=" + if len(mail.Attachments) > 0 { - boundary := "my-boundary-779" + boundary := fmt.Sprint("gotify-boundary-", rand.Int63()) + msgRaw = "From: " + mail.From + "\r\n" + "To: " + mail.To + "\r\n" + "Subject: " + subject + "\r\n" + From 8b8d1058603a44589fa2d0a4d0bda2543d340516 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Sat, 22 Mar 2025 01:50:05 +0100 Subject: [PATCH 6/9] Add max email size --- cmd/config.go | 1 + web/mail.go | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index bcc0a14..54d74bb 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -9,6 +9,7 @@ func loadConfig() error { viper.SetDefault("debug-mode", false) viper.SetDefault("google-mail.keyfile", "gapps.json") viper.SetDefault("mock-mode", false) + viper.SetDefault("max-mail-size", 20e6) viper.SetEnvPrefix("gotify") viper.AutomaticEnv() diff --git a/web/mail.go b/web/mail.go index 58adbd5..829bdcc 100644 --- a/web/mail.go +++ b/web/mail.go @@ -2,17 +2,25 @@ package web import ( "encoding/json" + "io" + "net/http" + "github.com/cthit/gotify" "github.com/gocraft/web" - "io/ioutil" - "net/http" + "github.com/spf13/viper" ) func (c *Context) SendMail(rw web.ResponseWriter, req *web.Request) { var mail gotify.Mail + // Ensure that the request is not too large + if req.ContentLength > viper.GetInt64("max-mail-size") { + rw.WriteHeader(http.StatusRequestEntityTooLarge) + return + } + // Read request body - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) req.Body.Close() if err != nil { c.printError(err) From c20452506c04edb548b1f86b1cfe8c72b9b0c387 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Sat, 22 Mar 2025 01:54:02 +0100 Subject: [PATCH 7/9] Add documentation --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a3a139..e934fd5 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,13 @@ Json Request: "to": "....", "from": "....", "subject": "....", - "body": "...." + "body": "....", + "attachments": [ + { + "name": "....", + "data": "...." + } + ] } ``` @@ -40,6 +46,7 @@ port = "8080" pre-shared-key = "......" debug-mode = false mock-mode = false +max-mail-size = 20000000 [google-mail] keyfile = "gapps.json" @@ -51,6 +58,7 @@ See [Environment Variables](#environment-variables) for config explanation * `GOTIFY_PORT`: Port for the web service, defaults to `8080` (string) * `GOTIFY_PRE-SHARED-KEY`*: Random string used by other apps to authenticate * `GOTIFY_DEBUG-MODE`: Bool indicating debug mode defaults to `false` +* `GOTIFY_MAX-MAIL-SIZE`: The maximum size of an email in bytes, defaults to `20000000` (int) * `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json` * `GOTIFY_GOOGLE-MAIL.ADMIN-MAIL`*: The google administrator email. From d35a542cb4331131785098c7824cdd58e4abbc41 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Sat, 22 Mar 2025 01:54:41 +0100 Subject: [PATCH 8/9] Set proper target branch --- .github/workflows/docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ce7ac4b..5a3b03a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,11 +2,11 @@ name: Docker on: push: - branches: [main] + branches: [master] tags: - v[0-9]+.[0-9]+.[0-9]+* pull_request: - branches: [main] + branches: [master] jobs: build: From 455133c422a72a283e042ea6f8343335db2c4129 Mon Sep 17 00:00:00 2001 From: Goostaf Date: Tue, 25 Mar 2025 12:02:20 +0100 Subject: [PATCH 9/9] Properly specify input content type --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e934fd5..8b88062 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ All request must inclue a header with the preshared key. POST `/mail` Json Request: -``` +```json5 { "to": "....", "from": "....", @@ -24,8 +24,9 @@ Json Request: "body": "....", "attachments": [ { - "name": "....", - "data": "...." + "name": "....", // File name + "data": "....", // Base64 encoded file + "content_type": "...." // MIME type } ] }