Esempio n. 1
0
// subscribeCmd handles subscribe command - clients send this when subscribe
// on channel, if channel if private then we must validate provided sign here before
// actually subscribe client on channel
func (c *client) subscribeCmd(cmd *SubscribeClientCommand) (*response, error) {

	resp := newResponse("subscribe")

	channel := cmd.Channel
	if channel == "" {
		return nil, ErrInvalidMessage
	}

	c.app.RLock()
	secret := c.app.config.Secret
	maxChannelLength := c.app.config.MaxChannelLength
	insecure := c.app.config.Insecure
	c.app.RUnlock()

	if len(channel) > maxChannelLength {
		resp.Err(ErrLimitExceeded)
		return resp, nil
	}

	body := &SubscribeBody{
		Channel: channel,
	}
	resp.Body = body

	if !c.app.userAllowed(channel, c.User) || !c.app.clientAllowed(channel, c.UID) {
		resp.Err(ErrPermissionDenied)
		return resp, nil
	}

	chOpts, err := c.app.channelOpts(channel)
	if err != nil {
		resp.Err(err)
		return resp, nil
	}

	if !chOpts.Anonymous && c.User == "" && !insecure {
		resp.Err(ErrPermissionDenied)
		return resp, nil
	}

	if c.app.privateChannel(channel) {
		// private channel - subscription must be properly signed
		if string(c.UID) != string(cmd.Client) {
			resp.Err(ErrPermissionDenied)
			return resp, nil
		}
		isValid := auth.CheckChannelSign(secret, string(cmd.Client), string(channel), cmd.Info, cmd.Sign)
		if !isValid {
			resp.Err(ErrPermissionDenied)
			return resp, nil
		}
		c.channelInfo[channel] = []byte(cmd.Info)
	}

	c.Channels[channel] = true

	info := c.info(channel)

	err = c.app.addSub(channel, c)
	if err != nil {
		logger.ERROR.Println(err)
		return resp, ErrInternalServerError
	}

	if chOpts.Presence {
		err = c.app.addPresence(channel, c.UID, info)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, ErrInternalServerError
		}
	}

	if chOpts.JoinLeave {
		err = c.app.pubJoinLeave(channel, "join", info)
		if err != nil {
			logger.ERROR.Println(err)
		}
	}

	if c.app.mediator != nil {
		c.app.mediator.Subscribe(channel, c.UID, c.User)
	}

	body.Status = true

	return resp, nil
}
Esempio n. 2
0
// subscribeCmd handles subscribe command - clients send this when subscribe
// on channel, if channel if private then we must validate provided sign here before
// actually subscribe client on channel. Optionally we can send missed messages to
// client if it provided last message id seen in channel.
func (c *client) subscribeCmd(cmd *SubscribeClientCommand) (*clientResponse, error) {

	resp := newClientResponse("subscribe")

	channel := cmd.Channel
	if channel == "" {
		return nil, ErrInvalidMessage
	}

	c.app.RLock()
	secret := c.app.config.Secret
	maxChannelLength := c.app.config.MaxChannelLength
	channelLimit := c.app.config.ClientChannelLimit
	insecure := c.app.config.Insecure
	c.app.RUnlock()

	if len(channel) > maxChannelLength {
		logger.ERROR.Printf("channel too long: max %d, got %d", maxChannelLength, len(channel))
		resp.Err(clientError{ErrLimitExceeded, errorAdviceFix})
		return resp, nil
	}

	if len(c.Channels) >= channelLimit {
		logger.ERROR.Printf("maximimum limit of channels per client reached: %d", channelLimit)
		resp.Err(clientError{ErrLimitExceeded, errorAdviceFix})
		return resp, nil
	}

	body := &SubscribeBody{
		Channel: channel,
	}
	resp.Body = body

	if _, ok := c.Channels[channel]; ok {
		resp.Err(clientError{ErrAlreadySubscribed, errorAdviceFix})
		return resp, nil
	}

	if !c.app.userAllowed(channel, c.User) || !c.app.clientAllowed(channel, c.UID) {
		resp.Err(clientError{ErrPermissionDenied, errorAdviceFix})
		return resp, nil
	}

	chOpts, err := c.app.channelOpts(channel)
	if err != nil {
		resp.Err(clientError{err, errorAdviceFix})
		return resp, nil
	}

	if !chOpts.Anonymous && c.User == "" && !insecure {
		resp.Err(clientError{ErrPermissionDenied, errorAdviceFix})
		return resp, nil
	}

	if c.app.privateChannel(channel) {
		// private channel - subscription must be properly signed
		if string(c.UID) != string(cmd.Client) {
			resp.Err(clientError{ErrPermissionDenied, errorAdviceFix})
			return resp, nil
		}
		isValid := auth.CheckChannelSign(secret, string(cmd.Client), string(channel), cmd.Info, cmd.Sign)
		if !isValid {
			resp.Err(clientError{ErrPermissionDenied, errorAdviceFix})
			return resp, nil
		}
		c.channelInfo[channel] = []byte(cmd.Info)
	}

	c.Channels[channel] = true

	info := c.info(channel)

	err = c.app.addSub(channel, c)
	if err != nil {
		logger.ERROR.Println(err)
		return resp, ErrInternalServerError
	}

	if chOpts.Presence {
		err = c.app.addPresence(channel, c.UID, info)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, ErrInternalServerError
		}
	}

	if chOpts.Recover {
		if cmd.Recover {
			// Client provided subscribe request with recover flag on. Try to recover missed messages
			// automatically from history (we suppose here that history configured wisely) based on
			// provided last message id value.
			messages, err := c.app.History(channel)
			if err != nil {
				logger.ERROR.Printf("can't recover messages for channel %s: %s", string(channel), err)
				body.Messages = []Message{}
			} else {
				recoveredMessages, recovered := recoverMessages(cmd.Last, messages)
				body.Messages = recoveredMessages
				body.Recovered = recovered
			}
		} else {
			// Client don't want to recover messages yet, we just return last message id to him here.
			lastMessageID, err := c.app.lastMessageID(channel)
			if err != nil {
				logger.ERROR.Println(err)
			} else {
				body.Last = lastMessageID
			}
		}
	}

	if chOpts.JoinLeave {
		go func() {
			err = c.app.pubJoinLeave(channel, "join", info)
			if err != nil {
				logger.ERROR.Println(err)
			}
		}()
	}

	if c.app.mediator != nil {
		c.app.mediator.Subscribe(channel, c.UID, c.User)
	}

	body.Status = true

	return resp, nil
}