@@ -13,6 +13,7 @@ import (
1313 "strings"
1414 "time"
1515
16+ "github.com/SherClockHolmes/webpush-go"
1617 "github.com/emersion/go-sasl"
1718 "golang.org/x/crypto/bcrypt"
1819 "gopkg.in/irc.v3"
@@ -232,6 +233,7 @@ var permanentDownstreamCaps = map[string]string{
232233 "soju.im/bouncer-networks" : "" ,
233234 "soju.im/bouncer-networks-notify" : "" ,
234235 "soju.im/read" : "" ,
236+ "soju.im/webpush" : "" ,
235237}
236238
237239// needAllDownstreamCaps is the list of downstream capabilities that
@@ -313,7 +315,8 @@ type downstreamConn struct {
313315
314316 lastBatchRef uint64
315317
316- monitored casemapMap
318+ monitored casemapMap
319+ webPushSubscriptions []webpush.Subscription
317320}
318321
319322func newDownstreamConn (srv * Server , ic ircConn , id uint64 ) * downstreamConn {
@@ -2997,6 +3000,83 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
29973000 Params : []string {"BOUNCER" , "UNKNOWN_COMMAND" , subcommand , "Unknown subcommand" },
29983001 }}
29993002 }
3003+ case "WEBPUSH" :
3004+ var subcommand string
3005+ if err := parseMessageParams (msg , & subcommand ); err != nil {
3006+ return err
3007+ }
3008+
3009+ switch subcommand {
3010+ case "VAPIDPUBKEY" :
3011+ dc .SendMessage (& irc.Message {
3012+ Prefix : dc .srv .prefix (),
3013+ Command : "WEBPUSH" ,
3014+ Params : []string {"VAPIDPUBKEY" , dc .srv .vapidKeys .pub },
3015+ })
3016+ case "REGISTER" :
3017+ var endpoint , keysStr string
3018+ if err := parseMessageParams (msg , nil , & endpoint , & keysStr ); err != nil {
3019+ return err
3020+ }
3021+
3022+ // TODO: validate endpoint URL
3023+ rawKeys := irc .ParseTags (keysStr )
3024+ authKey , hasAuthKey := rawKeys ["auth" ]
3025+ p256dhKey , hasP256dh := rawKeys ["p256dh" ]
3026+ if ! hasAuthKey || ! hasP256dh {
3027+ return ircError {& irc.Message {
3028+ Command : "FAIL" ,
3029+ Params : []string {"WEBPUSH" , "INVALID_PARAMS" , subcommand , "Missing auth or p256dh key" },
3030+ }}
3031+ }
3032+
3033+ keys := webpush.Keys {
3034+ Auth : string (authKey ),
3035+ P256dh : string (p256dhKey ),
3036+ }
3037+
3038+ for i , sub := range dc .user .webPushSubscriptions {
3039+ if sub .Endpoint == endpoint && sub .network == dc .network {
3040+ dc .user .webPushSubscriptions = append (dc .user .webPushSubscriptions [:i ], dc .user .webPushSubscriptions [i + 1 :]... )
3041+ break
3042+ }
3043+ }
3044+
3045+ // TODO: limit max number of subscriptions, prune old ones
3046+ // TODO: save the subscription in the DB
3047+
3048+ sub := & webPushSubscription {
3049+ Subscription : webpush.Subscription {
3050+ Endpoint : endpoint ,
3051+ Keys : keys ,
3052+ },
3053+ network : dc .network ,
3054+ }
3055+ dc .user .webPushSubscriptions = append (dc .user .webPushSubscriptions , sub )
3056+
3057+ dc .srv .sendWebPush (& sub .Subscription , & irc.Message {
3058+ Prefix : dc .srv .prefix (),
3059+ Command : "NOTE" ,
3060+ Params : []string {"WEBPUSH" , "REGISTERED" , "Web Push subscription created" },
3061+ })
3062+ case "UNREGISTER" :
3063+ var endpoint string
3064+ if err := parseMessageParams (msg , nil , & endpoint ); err != nil {
3065+ return err
3066+ }
3067+
3068+ for i , sub := range dc .user .webPushSubscriptions {
3069+ if sub .Endpoint == endpoint && sub .network == dc .network {
3070+ dc .user .webPushSubscriptions = append (dc .user .webPushSubscriptions [:i ], dc .user .webPushSubscriptions [i + 1 :]... )
3071+ break
3072+ }
3073+ }
3074+ default :
3075+ return ircError {& irc.Message {
3076+ Command : "FAIL" ,
3077+ Params : []string {"WEBPUSH" , "INVALID_PARAMS" , subcommand , "Unknown command" },
3078+ }}
3079+ }
30003080 default :
30013081 dc .logger .Printf ("unhandled message: %v" , msg )
30023082
0 commit comments