Skip to content

Commit 18bc805

Browse files
authored
Support for attachments and UTF-8 subjects (#9)
* Add module files * Add support for attachments, UTF-8 subjects * Add workflow * Improve Dockerfile * More random boundary, fix deprecated client * Add max email size * Add documentation * Set proper target branch * Properly specify input content type
1 parent b0894bb commit 18bc805

File tree

9 files changed

+285
-27
lines changed

9 files changed

+285
-27
lines changed

.github/workflows/docker.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Docker
2+
3+
on:
4+
push:
5+
branches: [master]
6+
tags:
7+
- v[0-9]+.[0-9]+.[0-9]+*
8+
pull_request:
9+
branches: [master]
10+
11+
jobs:
12+
build:
13+
name: Build image
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Docker Buildx
21+
uses: docker/setup-buildx-action@v3
22+
23+
- name: Login to GitHub Container Registry
24+
uses: docker/login-action@v3
25+
with:
26+
registry: ghcr.io
27+
username: ${{ github.repository_owner }}
28+
password: ${{ secrets.GITHUB_TOKEN }}
29+
30+
- name: Extract metadata (tags, labels) for Docker
31+
id: meta
32+
uses: docker/metadata-action@v5
33+
with:
34+
images: ghcr.io/${{ github.repository_owner }}/gotify
35+
tags: |
36+
type=semver,pattern={{version}}
37+
type=semver,pattern={{major}}.{{minor}}
38+
type=semver,pattern={{major}}
39+
type=sha
40+
type=edge
41+
type=ref,event=pr
42+
43+
- name: Build Docker image (and push on main)
44+
uses: docker/build-push-action@v6
45+
with:
46+
push: true
47+
tags: ${{ steps.meta.outputs.tags }}
48+
labels: ${{ steps.meta.outputs.labels }}
49+
cache-from: type=gha
50+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Dockerfile for gotify production
2-
FROM golang:alpine AS buildStage
3-
MAINTAINER digIT <digit@chalmers.it>
2+
FROM golang:alpine3.21 AS buildStage
3+
LABEL maintainer="digIT <digit@chalmers.it>"
44

55
# Install git
66
RUN apk update
@@ -12,18 +12,15 @@ RUN mkdir -p $GOPATH/src/github.com/cthit/gotify
1212
COPY . $GOPATH/src/github.com/cthit/gotify
1313
WORKDIR $GOPATH/src/github.com/cthit/gotify/cmd
1414

15-
# Grab dependencies
16-
RUN go get -d -v ./...
17-
18-
# build binary
15+
# Build binary
1916
RUN go install -v
2017
RUN mkdir /app && mv $GOPATH/bin/cmd /app/gotify
2118

2219
##########################
2320
# PRODUCTION STAGE #
2421
##########################
2522
FROM alpine
26-
MAINTAINER digIT <digit@chalmers.it>
23+
LABEL maintainer="digIT <digit@chalmers.it>"
2724

2825
# Add standard certificates
2926
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@ All request must inclue a header with the preshared key.
1616
POST `/mail`
1717

1818
Json Request:
19-
```
19+
```json5
2020
{
2121
"to": "....",
2222
"from": "....",
2323
"subject": "....",
24-
"body": "...."
24+
"body": "....",
25+
"attachments": [
26+
{
27+
"name": "....", // File name
28+
"data": "....", // Base64 encoded file
29+
"content_type": "...." // MIME type
30+
}
31+
]
2532
}
2633
```
2734

@@ -40,6 +47,7 @@ port = "8080"
4047
pre-shared-key = "......"
4148
debug-mode = false
4249
mock-mode = false
50+
max-mail-size = 20000000
4351
4452
[google-mail]
4553
keyfile = "gapps.json"
@@ -51,6 +59,7 @@ See [Environment Variables](#environment-variables) for config explanation
5159
* `GOTIFY_PORT`: Port for the web service, defaults to `8080` (string)
5260
* `GOTIFY_PRE-SHARED-KEY`*: Random string used by other apps to authenticate
5361
* `GOTIFY_DEBUG-MODE`: Bool indicating debug mode defaults to `false`
62+
* `GOTIFY_MAX-MAIL-SIZE`: The maximum size of an email in bytes, defaults to `20000000` (int)
5463
* `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json`
5564
* `GOTIFY_GOOGLE-MAIL.ADMIN-MAIL`*: The google administrator email.
5665

cmd/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ func loadConfig() error {
99
viper.SetDefault("debug-mode", false)
1010
viper.SetDefault("google-mail.keyfile", "gapps.json")
1111
viper.SetDefault("mock-mode", false)
12+
viper.SetDefault("max-mail-size", 20e6)
1213

1314
viper.SetEnvPrefix("gotify")
1415
viper.AutomaticEnv()

go.mod

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
module github.com/cthit/gotify
2+
3+
go 1.23.0
4+
5+
toolchain go1.23.7
6+
7+
require (
8+
github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b
9+
github.com/spf13/viper v1.20.0
10+
golang.org/x/net v0.37.0
11+
golang.org/x/oauth2 v0.28.0
12+
google.golang.org/api v0.227.0
13+
)
14+
15+
require (
16+
cloud.google.com/go/auth v0.15.0 // indirect
17+
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
18+
cloud.google.com/go/compute/metadata v0.6.0 // indirect
19+
github.com/felixge/httpsnoop v1.0.4 // indirect
20+
github.com/fsnotify/fsnotify v1.8.0 // indirect
21+
github.com/go-logr/logr v1.4.2 // indirect
22+
github.com/go-logr/stdr v1.2.2 // indirect
23+
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
24+
github.com/google/s2a-go v0.1.9 // indirect
25+
github.com/google/uuid v1.6.0 // indirect
26+
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
27+
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
28+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
29+
github.com/sagikazarmark/locafero v0.7.0 // indirect
30+
github.com/sourcegraph/conc v0.3.0 // indirect
31+
github.com/spf13/afero v1.12.0 // indirect
32+
github.com/spf13/cast v1.7.1 // indirect
33+
github.com/spf13/pflag v1.0.6 // indirect
34+
github.com/subosito/gotenv v1.6.0 // indirect
35+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
36+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
37+
go.opentelemetry.io/otel v1.34.0 // indirect
38+
go.opentelemetry.io/otel/metric v1.34.0 // indirect
39+
go.opentelemetry.io/otel/trace v1.34.0 // indirect
40+
go.uber.org/atomic v1.9.0 // indirect
41+
go.uber.org/multierr v1.9.0 // indirect
42+
golang.org/x/crypto v0.36.0 // indirect
43+
golang.org/x/sys v0.31.0 // indirect
44+
golang.org/x/text v0.23.0 // indirect
45+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
46+
google.golang.org/grpc v1.71.0 // indirect
47+
google.golang.org/protobuf v1.36.5 // indirect
48+
gopkg.in/yaml.v3 v3.0.1 // indirect
49+
)

go.sum

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
2+
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
3+
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
4+
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
5+
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
6+
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
7+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10+
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
11+
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
12+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
13+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
14+
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
15+
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
16+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
17+
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
18+
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
19+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
20+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
21+
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
22+
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
23+
github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b h1:g2Qcs0B+vOQE1L3a7WQ/JUUSzJnHbTz14qkJSqEWcF4=
24+
github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b/go.mod h1:Ag7UMbZNGrnHwaXPJOUKJIVgx4QOWMOWZngrvsN6qak=
25+
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
26+
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
27+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
28+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
29+
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
30+
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
31+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
32+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
33+
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
34+
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
35+
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
36+
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
37+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
38+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
39+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
40+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
41+
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
42+
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
43+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
44+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
45+
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
46+
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
47+
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
48+
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
49+
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
50+
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
51+
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
52+
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
53+
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
54+
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
55+
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
56+
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
57+
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
58+
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
59+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
60+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
61+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
62+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
63+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
64+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
65+
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
66+
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
67+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
68+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
69+
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
70+
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
71+
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
72+
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
73+
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
74+
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
75+
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
76+
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
77+
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
78+
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
79+
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
80+
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
81+
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
82+
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
83+
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
84+
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
85+
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
86+
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
87+
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
88+
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
89+
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
90+
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
91+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
92+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
93+
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
94+
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
95+
google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
96+
google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
97+
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
98+
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
99+
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
100+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
101+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
102+
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
103+
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
104+
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
105+
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
106+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
107+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
108+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
109+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
110+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

google_mail/google_service.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package google_mail
22

33
import (
4+
"fmt"
5+
46
"google.golang.org/api/gmail/v1" // Imports as gmail
7+
"google.golang.org/api/option"
58

69
"golang.org/x/net/context"
710
"golang.org/x/oauth2/google"
811

912
"encoding/base64"
10-
"io/ioutil"
13+
"os"
1114

1215
"github.com/cthit/gotify"
16+
17+
"math/rand"
1318
)
1419

1520
type googleService struct {
@@ -20,7 +25,7 @@ type googleService struct {
2025

2126
func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) (func() gotify.MailService, error) {
2227

23-
jsonKey, err := ioutil.ReadFile(keyPath)
28+
jsonKey, err := os.ReadFile(keyPath)
2429
if err != nil {
2530
return nil, err
2631
}
@@ -37,7 +42,7 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) (
3742
// Create a http client
3843
client := config.Client(context.Background())
3944

40-
mailService, err := gmail.New(client)
45+
mailService, err := gmail.NewService(context.Background(), option.WithHTTPClient(client))
4146
if err != nil {
4247
return nil, err
4348
}
@@ -47,9 +52,6 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) (
4752
adminMail: adminMail,
4853
debug: debug,
4954
}
50-
if err != nil {
51-
return nil, err
52-
}
5355

5456
return func() gotify.MailService {
5557
return gs
@@ -59,11 +61,36 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) (
5961
func (g *googleService) SendMail(mail gotify.Mail) (gotify.Mail, error) {
6062

6163
mail.From = g.adminMail
62-
63-
msgRaw := "From: " + mail.From + "\r\n" +
64-
"To: " + mail.To + "\r\n" +
65-
"Subject: " + mail.Subject + "\r\n\r\n" +
66-
mail.Body + "\r\n"
64+
var msgRaw string
65+
subject := "=?UTF-8?B?" + base64.StdEncoding.EncodeToString([]byte(mail.Subject)) + "?="
66+
67+
if len(mail.Attachments) > 0 {
68+
boundary := fmt.Sprint("gotify-boundary-", rand.Int63())
69+
70+
msgRaw = "From: " + mail.From + "\r\n" +
71+
"To: " + mail.To + "\r\n" +
72+
"Subject: " + subject + "\r\n" +
73+
"MIME-Version: 1.0\r\n" +
74+
"Content-Type: multipart/mixed; boundary=" + boundary + "\r\n\r\n" +
75+
"--" + boundary + "\r\n" +
76+
"Content-Type: text/plain; charset=UTF-8\r\n\r\n" +
77+
mail.Body + "\r\n"
78+
79+
for _, attachment := range mail.Attachments {
80+
msgRaw += "--" + boundary + "\r\n" +
81+
"Content-Type: " + attachment.ContentType + "; name=\"" + attachment.Name + "\"\r\n" +
82+
"Content-Disposition: attachment; filename=\"" + attachment.Name + "\"\r\n" +
83+
"Content-Transfer-Encoding: base64\r\n\r\n" +
84+
attachment.Data + "\r\n"
85+
}
86+
87+
msgRaw += "--" + boundary + "--"
88+
} else {
89+
msgRaw = "From: " + mail.From + "\r\n" +
90+
"To: " + mail.To + "\r\n" +
91+
"Subject: " + subject + "\r\n\r\n" +
92+
mail.Body + "\r\n"
93+
}
6794

6895
msg := &gmail.Message{
6996
Raw: base64.RawURLEncoding.EncodeToString([]byte(msgRaw)),

mail.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
package gotify
22

33
type Mail struct {
4-
To string `json:"to"`
5-
From string `json:"from"`
6-
Subject string `json:"subject"`
7-
Body string `json:"body"`
4+
To string `json:"to"`
5+
From string `json:"from"`
6+
Subject string `json:"subject"`
7+
Body string `json:"body"`
8+
Attachments []Attachment `json:"attachments"`
9+
}
10+
11+
type Attachment struct {
12+
Name string `json:"name"`
13+
Data string `json:"data"`
14+
ContentType string `json:"content_type"`
815
}

0 commit comments

Comments
 (0)