forked from lug/matterbridge
		
	Compare commits
	
		
			125 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4b0bc6d0bf | ||
| 
						 | 
					8c0b04b995 | ||
| 
						 | 
					e5989adf92 | ||
| 
						 | 
					9e5da2f9d7 | ||
| 
						 | 
					a284a228a3 | ||
| 
						 | 
					2133e0d1be | ||
| 
						 | 
					a6f37f1d61 | ||
| 
						 | 
					9de9151826 | ||
| 
						 | 
					fdd5ada98c | ||
| 
						 | 
					80fcf18e24 | ||
| 
						 | 
					ab94b5ca7a | ||
| 
						 | 
					8d2ce56c37 | ||
| 
						 | 
					1ec324354b | ||
| 
						 | 
					16be6601c8 | ||
| 
						 | 
					98027446c8 | ||
| 
						 | 
					f2f1d874e1 | ||
| 
						 | 
					25a72113b1 | ||
| 
						 | 
					79c4ad5015 | ||
| 
						 | 
					e24f1c7c87 | ||
| 
						 | 
					dbf8a326d5 | ||
| 
						 | 
					0bc9c70c66 | ||
| 
						 | 
					594d2155e3 | ||
| 
						 | 
					20dbd71306 | ||
| 
						 | 
					6a727b9723 | ||
| 
						 | 
					02a5bc096f | ||
| 
						 | 
					2110db6f0c | ||
| 
						 | 
					2bac867382 | ||
| 
						 | 
					5fbd8a3be0 | ||
| 
						 | 
					ad6440b603 | ||
| 
						 | 
					064b6a915f | ||
| 
						 | 
					1578ebb0e2 | ||
| 
						 | 
					73525a4bbc | ||
| 
						 | 
					d62f49d1fc | ||
| 
						 | 
					63b88e77f2 | ||
| 
						 | 
					3d8f15c20b | ||
| 
						 | 
					cac5d56d60 | ||
| 
						 | 
					bd2a672c14 | ||
| 
						 | 
					82396e73f5 | ||
| 
						 | 
					ba928b169d | ||
| 
						 | 
					4fed720f97 | ||
| 
						 | 
					78238c85d4 | ||
| 
						 | 
					4f2ae7b73f | ||
| 
						 | 
					f82a9cc7ac | ||
| 
						 | 
					cce7624ab8 | ||
| 
						 | 
					c5ecd09172 | ||
| 
						 | 
					7b21c1c2f4 | ||
| 
						 | 
					f8714d81f5 | ||
| 
						 | 
					8622656005 | ||
| 
						 | 
					52237fadb6 | ||
| 
						 | 
					222cccf388 | ||
| 
						 | 
					bab308508e | ||
| 
						 | 
					dedb83c867 | ||
| 
						 | 
					723a90cdd6 | ||
| 
						 | 
					67d2398fa8 | ||
| 
						 | 
					5f3b6ec007 | ||
| 
						 | 
					55ab0c12f1 | ||
| 
						 | 
					d1227b5fc9 | ||
| 
						 | 
					6ea368c383 | ||
| 
						 | 
					e92b6de09f | ||
| 
						 | 
					e622587db4 | ||
| 
						 | 
					f2efc06d1f | ||
| 
						 | 
					a2b94452db | ||
| 
						 | 
					4c506f7cc3 | ||
| 
						 | 
					7886f05e88 | ||
| 
						 | 
					f58be0d1c1 | ||
| 
						 | 
					1152394bc1 | ||
| 
						 | 
					a082b5a590 | ||
| 
						 | 
					bae9484df2 | ||
| 
						 | 
					6f78485878 | ||
| 
						 | 
					fd0fe3390b | ||
| 
						 | 
					2522158127 | ||
| 
						 | 
					8be107cecc | ||
| 
						 | 
					5aab158c0b | ||
| 
						 | 
					1d33e60e36 | ||
| 
						 | 
					83c28cb857 | ||
| 
						 | 
					df5bce27b0 | ||
| 
						 | 
					2b15739b48 | ||
| 
						 | 
					3480c88e90 | ||
| 
						 | 
					432cd0f99d | ||
| 
						 | 
					e8b3e9b22d | ||
| 
						 | 
					d4a47671ea | ||
| 
						 | 
					0bcd1e62f3 | ||
| 
						 | 
					80822b7fff | ||
| 
						 | 
					78f1011f52 | ||
| 
						 | 
					67f6257617 | ||
| 
						 | 
					169c614489 | ||
| 
						 | 
					da908c438a | ||
| 
						 | 
					9c9c4bf1f9 | ||
| 
						 | 
					7764493298 | ||
| 
						 | 
					64a20ee61b | ||
| 
						 | 
					62d1af8c37 | ||
| 
						 | 
					0f5274fdf6 | ||
| 
						 | 
					2e2187ebf4 | ||
| 
						 | 
					762c3350f4 | ||
| 
						 | 
					e1a4d7f77e | ||
| 
						 | 
					a7a4554a85 | ||
| 
						 | 
					6bd808ce91 | ||
| 
						 | 
					a5c143bc46 | ||
| 
						 | 
					87c9cac756 | ||
| 
						 | 
					6a047f8722 | ||
| 
						 | 
					6523494e83 | ||
| 
						 | 
					7c6ce8bb90 | ||
| 
						 | 
					dafbfe4021 | ||
| 
						 | 
					a4d5c94d9b | ||
| 
						 | 
					7119e378a7 | ||
| 
						 | 
					e1dc3032c1 | ||
| 
						 | 
					5de03b8921 | ||
| 
						 | 
					7631d43c48 | ||
| 
						 | 
					d0b2ee5c85 | ||
| 
						 | 
					8830a5a1df | ||
| 
						 | 
					ee87626a93 | ||
| 
						 | 
					9f15d38c1c | ||
| 
						 | 
					4a96a977c0 | ||
| 
						 | 
					9a95293bdf | ||
| 
						 | 
					0b3a06d263 | ||
| 
						 | 
					9a6249c4f5 | ||
| 
						 | 
					50bd51e461 | ||
| 
						 | 
					04f8013314 | ||
| 
						 | 
					a0aaf0057a | ||
| 
						 | 
					8e78b3e6be | ||
| 
						 | 
					57a503818d | ||
| 
						 | 
					25d2ff3e9b | ||
| 
						 | 
					31902d3e57 | ||
| 
						 | 
					16f3fa6bae | ||
| 
						 | 
					1f706673cf | 
							
								
								
									
										28
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -1,22 +1,36 @@
 | 
				
			|||||||
If you have a configuration problem, please first try to create a basic configuration following the instructions on [the wiki](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) before filing an issue.
 | 
					<!-- This is a bug report template. By following the instructions below and
 | 
				
			||||||
 | 
					filling out the sections with your information, you will help the us to get all
 | 
				
			||||||
 | 
					the necessary data to fix your issue.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Please answer the following questions. 
 | 
					You can also preview your report before submitting it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Which version of matterbridge are you using?
 | 
					Text between <!-- and --> marks will be invisible in the report.
 | 
				
			||||||
run ```matterbridge -version```
 | 
					-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### If you're having problems with mattermost please specify mattermost version. 
 | 
					<!-- If you have a configuration problem, please first try to create a basic configuration following the instructions on [the wiki](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) before filing an issue. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Environment
 | 
				
			||||||
 | 
					<!-- run `matterbridge -version` -->
 | 
				
			||||||
 | 
					<!-- If you're having problems with mattermost also specify the mattermost version. -->
 | 
				
			||||||
 | 
					Version:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- What operating system are you using ? (be as specific as possible) -->
 | 
				
			||||||
 | 
					Operating system:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- If you compiled matterbridge yourself:
 | 
				
			||||||
 | 
					       * Specify the output of `go version` 
 | 
				
			||||||
 | 
					       * Specify the output of `git rev-parse HEAD` -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Please describe the expected behavior.
 | 
					### Please describe the expected behavior.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Please describe the actual behavior. 
 | 
					### Please describe the actual behavior. 
 | 
				
			||||||
#### Use logs from running ```matterbridge -debug``` if possible.
 | 
					<!-- Use logs from running `matterbridge -debug` if possible. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Any steps to reproduce the behavior?
 | 
					### Any steps to reproduce the behavior?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Please add your configuration file 
 | 
					### Please add your configuration file 
 | 
				
			||||||
#### (be sure to exclude or anonymize private data (tokens/passwords))
 | 
					<!-- (be sure to exclude or anonymize private data (tokens/passwords)) -->
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
language: go
 | 
					language: go
 | 
				
			||||||
go:
 | 
					go:
 | 
				
			||||||
    #- 1.7.x
 | 
					    #- 1.7.x
 | 
				
			||||||
    - 1.9.x
 | 
					    - 1.10.x
 | 
				
			||||||
      # - tip
 | 
					      # - tip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# we have everything vendored
 | 
					# we have everything vendored
 | 
				
			||||||
@@ -34,9 +34,9 @@ before_script:
 | 
				
			|||||||
# flunk the build and immediately stop. It's sorta like having
 | 
					# flunk the build and immediately stop. It's sorta like having
 | 
				
			||||||
# set -e enabled in bash. 
 | 
					# set -e enabled in bash. 
 | 
				
			||||||
script:
 | 
					script:
 | 
				
			||||||
  - test -z $(gofmt -s -l $GO_FILES)  # Fail if a .go file hasn't been formatted with gofmt
 | 
					 #- test -z $(gofmt -s -l $GO_FILES)  # Fail if a .go file hasn't been formatted with gofmt
 | 
				
			||||||
  - go test -v -race $PKGS            # Run all the tests with the race detector enabled
 | 
					  - go test -v -race $PKGS            # Run all the tests with the race detector enabled
 | 
				
			||||||
  - go vet $PKGS                      # go vet is the official Go static analyzer
 | 
					 #  - go vet $PKGS                      # go vet is the official Go static analyzer
 | 
				
			||||||
  - megacheck $PKGS                   # "go vet on steroids" + linter
 | 
					  - megacheck $PKGS                   # "go vet on steroids" + linter
 | 
				
			||||||
  - /bin/bash ci/bintray.sh
 | 
					  - /bin/bash ci/bintray.sh
 | 
				
			||||||
  #- golint -set_exit_status $PKGS     # one last linter
 | 
					  #- golint -set_exit_status $PKGS     # one last linter
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								README.md
									
									
									
									
									
								
							@@ -1,17 +1,18 @@
 | 
				
			|||||||
# matterbridge
 | 
					# matterbridge
 | 
				
			||||||
Click on one of the badges below to join the chat   
 | 
					Click on one of the badges below to join the chat   
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) 
 | 
					[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) [](https://inverse.chat) [](https://www.twitch.tv/matterbridge)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://github.com/42wim/matterbridge/releases/latest) [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
 | 
					[](https://github.com/42wim/matterbridge/releases/latest) [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp), Matrix and Steam.
 | 
					Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp), Matrix, Steam and ssh-chat
 | 
				
			||||||
Has a REST API.  
 | 
					Has a REST API.  
 | 
				
			||||||
 | 
					Minecraft server chat support via [MatterLink](https://github.com/elytra/MatterLink)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Table of Contents
 | 
					# Table of Contents
 | 
				
			||||||
 * [Features](#features)
 | 
					 * [Features](https://github.com/42wim/matterbridge/wiki/Features)
 | 
				
			||||||
 * [Requirements](#requirements)
 | 
					 * [Requirements](#requirements)
 | 
				
			||||||
 * [Screenshots](https://github.com/42wim/matterbridge/wiki/)
 | 
					 * [Screenshots](https://github.com/42wim/matterbridge/wiki/)
 | 
				
			||||||
 * [Installing](#installing)
 | 
					 * [Installing](#installing)
 | 
				
			||||||
@@ -27,13 +28,21 @@ Has a REST API.
 | 
				
			|||||||
 * [Thanks](#thanks)
 | 
					 * [Thanks](#thanks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Features
 | 
					# Features
 | 
				
			||||||
* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat (via xmpp), Matrix and Steam. 
 | 
					* [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols)
 | 
				
			||||||
  Pick and mix.
 | 
					* [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols)
 | 
				
			||||||
* Support private groups on your mattermost/slack.
 | 
					* [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes)
 | 
				
			||||||
* Allow for bridging the same bridges, which means you can eg bridge between multiple mattermosts.
 | 
					* [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling)
 | 
				
			||||||
* The bridge is now a gateway which has support multiple in and out bridges. (and supports multiple gateways).
 | 
					* [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing)
 | 
				
			||||||
* Edits and delete messages across bridges that support it (mattermost,slack,discord,gitter,telegram)
 | 
					* [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups)
 | 
				
			||||||
* REST API to read/post messages to bridges (WIP).
 | 
					* [API](https://github.com/42wim/matterbridge/wiki/Features#api)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## API
 | 
				
			||||||
 | 
					The API is very basic at the moment and rather undocumented.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Used by at least 2 projects. Feel free to make a PR to add your project to this list.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* [MatterLink](https://github.com/elytra/MatterLink) (Matterbridge link for Minecraft Server chat)
 | 
				
			||||||
 | 
					* [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Requirements
 | 
					# Requirements
 | 
				
			||||||
Accounts to one of the supported bridges
 | 
					Accounts to one of the supported bridges
 | 
				
			||||||
@@ -48,13 +57,15 @@ Accounts to one of the supported bridges
 | 
				
			|||||||
* [Rocket.chat](https://rocket.chat)
 | 
					* [Rocket.chat](https://rocket.chat)
 | 
				
			||||||
* [Matrix](https://matrix.org)
 | 
					* [Matrix](https://matrix.org)
 | 
				
			||||||
* [Steam](https://store.steampowered.com/)
 | 
					* [Steam](https://store.steampowered.com/)
 | 
				
			||||||
 | 
					* [Twitch](https://twitch.tv)
 | 
				
			||||||
 | 
					* [Ssh-chat](https://github.com/shazow/ssh-chat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Screenshots
 | 
					# Screenshots
 | 
				
			||||||
See https://github.com/42wim/matterbridge/wiki
 | 
					See https://github.com/42wim/matterbridge/wiki
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Installing
 | 
					# Installing
 | 
				
			||||||
## Binaries
 | 
					## Binaries
 | 
				
			||||||
* Latest stable release [v1.6.1](https://github.com/42wim/matterbridge/releases/latest)
 | 
					* Latest stable release [v1.9.0](https://github.com/42wim/matterbridge/releases/latest)
 | 
				
			||||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)  
 | 
					* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Building
 | 
					## Building
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,8 @@ package api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"github.com/labstack/echo"
 | 
						"github.com/labstack/echo"
 | 
				
			||||||
	"github.com/labstack/echo/middleware"
 | 
						"github.com/labstack/echo/middleware"
 | 
				
			||||||
	"github.com/zfjagann/golang-ring"
 | 
						"github.com/zfjagann/golang-ring"
 | 
				
			||||||
@@ -15,7 +15,7 @@ import (
 | 
				
			|||||||
type Api struct {
 | 
					type Api struct {
 | 
				
			||||||
	Messages ring.Ring
 | 
						Messages ring.Ring
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ApiMessage struct {
 | 
					type ApiMessage struct {
 | 
				
			||||||
@@ -26,28 +26,27 @@ type ApiMessage struct {
 | 
				
			|||||||
	Gateway  string `json:"gateway"`
 | 
						Gateway  string `json:"gateway"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "api"
 | 
						b := &Api{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Api {
 | 
					 | 
				
			||||||
	b := &Api{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
	e := echo.New()
 | 
						e := echo.New()
 | 
				
			||||||
 | 
						e.HideBanner = true
 | 
				
			||||||
 | 
						e.HidePort = true
 | 
				
			||||||
	b.Messages = ring.Ring{}
 | 
						b.Messages = ring.Ring{}
 | 
				
			||||||
	b.Messages.SetCapacity(b.Config.Buffer)
 | 
						b.Messages.SetCapacity(b.GetInt("Buffer"))
 | 
				
			||||||
	if b.Config.Token != "" {
 | 
						if b.GetString("Token") != "" {
 | 
				
			||||||
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
 | 
							e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
 | 
				
			||||||
			return key == b.Config.Token, nil
 | 
								return key == b.GetString("Token"), nil
 | 
				
			||||||
		}))
 | 
							}))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	e.GET("/api/messages", b.handleMessages)
 | 
						e.GET("/api/messages", b.handleMessages)
 | 
				
			||||||
	e.GET("/api/stream", b.handleStream)
 | 
						e.GET("/api/stream", b.handleStream)
 | 
				
			||||||
	e.POST("/api/message", b.handlePostMessage)
 | 
						e.POST("/api/message", b.handlePostMessage)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		flog.Fatal(e.Start(b.Config.BindAddress))
 | 
							if b.GetString("BindAddress") == "" {
 | 
				
			||||||
 | 
								b.Log.Fatalf("No BindAddress configured.")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							b.Log.Infof("Listening on %s", b.GetString("BindAddress"))
 | 
				
			||||||
 | 
							b.Log.Fatal(e.Start(b.GetString("BindAddress")))
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -76,21 +75,18 @@ func (b *Api) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Api) handlePostMessage(c echo.Context) error {
 | 
					func (b *Api) handlePostMessage(c echo.Context) error {
 | 
				
			||||||
	message := &ApiMessage{}
 | 
						message := config.Message{}
 | 
				
			||||||
	if err := c.Bind(message); err != nil {
 | 
						if err := c.Bind(&message); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
 | 
						// these values are fixed
 | 
				
			||||||
	b.Remote <- config.Message{
 | 
						message.Channel = "api"
 | 
				
			||||||
		Text:     message.Text,
 | 
						message.Protocol = "api"
 | 
				
			||||||
		Username: message.Username,
 | 
						message.Account = b.Account
 | 
				
			||||||
		UserID:   message.UserID,
 | 
						message.ID = ""
 | 
				
			||||||
		Channel:  "api",
 | 
						message.Timestamp = time.Now()
 | 
				
			||||||
		Avatar:   message.Avatar,
 | 
						b.Log.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
 | 
				
			||||||
		Account:  b.Account,
 | 
						b.Remote <- message
 | 
				
			||||||
		Gateway:  message.Gateway,
 | 
					 | 
				
			||||||
		Protocol: "api",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return c.JSON(http.StatusOK, message)
 | 
						return c.JSON(http.StatusOK, message)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										110
									
								
								bridge/bridge.go
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								bridge/bridge.go
									
									
									
									
									
								
							@@ -1,20 +1,8 @@
 | 
				
			|||||||
package bridge
 | 
					package bridge
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/api"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/discord"
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/gitter"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/irc"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/matrix"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/mattermost"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/rocketchat"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/slack"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/sshchat"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/steam"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/telegram"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/xmpp"
 | 
					 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -27,16 +15,28 @@ type Bridger interface {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Bridge struct {
 | 
					type Bridge struct {
 | 
				
			||||||
	Config config.Protocol
 | 
					 | 
				
			||||||
	Bridger
 | 
						Bridger
 | 
				
			||||||
	Name     string
 | 
						Name     string
 | 
				
			||||||
	Account  string
 | 
						Account  string
 | 
				
			||||||
	Protocol string
 | 
						Protocol string
 | 
				
			||||||
	Channels map[string]config.ChannelInfo
 | 
						Channels map[string]config.ChannelInfo
 | 
				
			||||||
	Joined   map[string]bool
 | 
						Joined   map[string]bool
 | 
				
			||||||
 | 
						Log      *log.Entry
 | 
				
			||||||
 | 
						Config   *config.Config
 | 
				
			||||||
 | 
						General  *config.Protocol
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge {
 | 
					type Config struct {
 | 
				
			||||||
 | 
						//	General *config.Protocol
 | 
				
			||||||
 | 
						Remote chan config.Message
 | 
				
			||||||
 | 
						Log    *log.Entry
 | 
				
			||||||
 | 
						*Bridge
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Factory is the factory function to create a bridge
 | 
				
			||||||
 | 
					type Factory func(*Config) Bridger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(bridge *config.Bridge) *Bridge {
 | 
				
			||||||
	b := new(Bridge)
 | 
						b := new(Bridge)
 | 
				
			||||||
	b.Channels = make(map[string]config.ChannelInfo)
 | 
						b.Channels = make(map[string]config.ChannelInfo)
 | 
				
			||||||
	accInfo := strings.Split(bridge.Account, ".")
 | 
						accInfo := strings.Split(bridge.Account, ".")
 | 
				
			||||||
@@ -46,49 +46,6 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid
 | 
				
			|||||||
	b.Protocol = protocol
 | 
						b.Protocol = protocol
 | 
				
			||||||
	b.Account = bridge.Account
 | 
						b.Account = bridge.Account
 | 
				
			||||||
	b.Joined = make(map[string]bool)
 | 
						b.Joined = make(map[string]bool)
 | 
				
			||||||
	bridgeConfig := &config.BridgeConfig{General: &cfg.General, Account: bridge.Account, Remote: c}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// override config from environment
 | 
					 | 
				
			||||||
	config.OverrideCfgFromEnv(cfg, protocol, name)
 | 
					 | 
				
			||||||
	switch protocol {
 | 
					 | 
				
			||||||
	case "mattermost":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Mattermost[name]
 | 
					 | 
				
			||||||
		b.Bridger = bmattermost.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "irc":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.IRC[name]
 | 
					 | 
				
			||||||
		b.Bridger = birc.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "gitter":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Gitter[name]
 | 
					 | 
				
			||||||
		b.Bridger = bgitter.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "slack":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Slack[name]
 | 
					 | 
				
			||||||
		b.Bridger = bslack.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "xmpp":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Xmpp[name]
 | 
					 | 
				
			||||||
		b.Bridger = bxmpp.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "discord":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Discord[name]
 | 
					 | 
				
			||||||
		b.Bridger = bdiscord.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "telegram":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Telegram[name]
 | 
					 | 
				
			||||||
		b.Bridger = btelegram.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "rocketchat":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Rocketchat[name]
 | 
					 | 
				
			||||||
		b.Bridger = brocketchat.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "matrix":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Matrix[name]
 | 
					 | 
				
			||||||
		b.Bridger = bmatrix.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "steam":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Steam[name]
 | 
					 | 
				
			||||||
		b.Bridger = bsteam.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "sshchat":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Sshchat[name]
 | 
					 | 
				
			||||||
		b.Bridger = bsshchat.New(bridgeConfig)
 | 
					 | 
				
			||||||
	case "api":
 | 
					 | 
				
			||||||
		bridgeConfig.Config = cfg.Api[name]
 | 
					 | 
				
			||||||
		b.Bridger = api.New(bridgeConfig)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	b.Config = bridgeConfig.Config
 | 
					 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,7 +57,7 @@ func (b *Bridge) JoinChannels() error {
 | 
				
			|||||||
func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map[string]bool) error {
 | 
					func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map[string]bool) error {
 | 
				
			||||||
	for ID, channel := range channels {
 | 
						for ID, channel := range channels {
 | 
				
			||||||
		if !exists[ID] {
 | 
							if !exists[ID] {
 | 
				
			||||||
			log.Infof("%s: joining %s (%s)", b.Account, channel.Name, ID)
 | 
								b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
 | 
				
			||||||
			err := b.JoinChannel(channel)
 | 
								err := b.JoinChannel(channel)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
@@ -110,3 +67,38 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) GetBool(key string) bool {
 | 
				
			||||||
 | 
						if b.Config.GetBool(b.Account + "." + key) {
 | 
				
			||||||
 | 
							return b.Config.GetBool(b.Account + "." + key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.Config.GetBool("general." + key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) GetInt(key string) int {
 | 
				
			||||||
 | 
						if b.Config.GetInt(b.Account+"."+key) != 0 {
 | 
				
			||||||
 | 
							return b.Config.GetInt(b.Account + "." + key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.Config.GetInt("general." + key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) GetString(key string) string {
 | 
				
			||||||
 | 
						if b.Config.GetString(b.Account+"."+key) != "" {
 | 
				
			||||||
 | 
							return b.Config.GetString(b.Account + "." + key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.Config.GetString("general." + key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) GetStringSlice(key string) []string {
 | 
				
			||||||
 | 
						if len(b.Config.GetStringSlice(b.Account+"."+key)) != 0 {
 | 
				
			||||||
 | 
							return b.Config.GetStringSlice(b.Account + "." + key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.Config.GetStringSlice("general." + key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) GetStringSlice2D(key string) [][]string {
 | 
				
			||||||
 | 
						if len(b.Config.GetStringSlice2D(b.Account+"."+key)) != 0 {
 | 
				
			||||||
 | 
							return b.Config.GetStringSlice2D(b.Account + "." + key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.Config.GetStringSlice2D("general." + key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,24 @@
 | 
				
			|||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/BurntSushi/toml"
 | 
						"bytes"
 | 
				
			||||||
	"log"
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	EVENT_JOIN_LEAVE      = "join_leave"
 | 
						EVENT_JOIN_LEAVE        = "join_leave"
 | 
				
			||||||
	EVENT_FAILURE         = "failure"
 | 
						EVENT_TOPIC_CHANGE      = "topic_change"
 | 
				
			||||||
	EVENT_REJOIN_CHANNELS = "rejoin_channels"
 | 
						EVENT_FAILURE           = "failure"
 | 
				
			||||||
	EVENT_USER_ACTION     = "user_action"
 | 
						EVENT_FILE_FAILURE_SIZE = "file_failure_size"
 | 
				
			||||||
	EVENT_MSG_DELETE      = "msg_delete"
 | 
						EVENT_AVATAR_DOWNLOAD   = "avatar_download"
 | 
				
			||||||
 | 
						EVENT_REJOIN_CHANNELS   = "rejoin_channels"
 | 
				
			||||||
 | 
						EVENT_USER_ACTION       = "user_action"
 | 
				
			||||||
 | 
						EVENT_MSG_DELETE        = "msg_delete"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Message struct {
 | 
					type Message struct {
 | 
				
			||||||
@@ -37,6 +41,9 @@ type FileInfo struct {
 | 
				
			|||||||
	Data    *[]byte
 | 
						Data    *[]byte
 | 
				
			||||||
	Comment string
 | 
						Comment string
 | 
				
			||||||
	URL     string
 | 
						URL     string
 | 
				
			||||||
 | 
						Size    int64
 | 
				
			||||||
 | 
						Avatar  bool
 | 
				
			||||||
 | 
						SHA     string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ChannelInfo struct {
 | 
					type ChannelInfo struct {
 | 
				
			||||||
@@ -53,12 +60,15 @@ type Protocol struct {
 | 
				
			|||||||
	BindAddress            string // mattermost, slack // DEPRECATED
 | 
						BindAddress            string // mattermost, slack // DEPRECATED
 | 
				
			||||||
	Buffer                 int    // api
 | 
						Buffer                 int    // api
 | 
				
			||||||
	Charset                string // irc
 | 
						Charset                string // irc
 | 
				
			||||||
 | 
						Debug                  bool   // general
 | 
				
			||||||
 | 
						DebugLevel             int    // only for irc now
 | 
				
			||||||
	EditSuffix             string // mattermost, slack, discord, telegram, gitter
 | 
						EditSuffix             string // mattermost, slack, discord, telegram, gitter
 | 
				
			||||||
	EditDisable            bool   // mattermost, slack, discord, telegram, gitter
 | 
						EditDisable            bool   // mattermost, slack, discord, telegram, gitter
 | 
				
			||||||
	IconURL                string // mattermost, slack
 | 
						IconURL                string // mattermost, slack
 | 
				
			||||||
	IgnoreNicks            string // all protocols
 | 
						IgnoreNicks            string // all protocols
 | 
				
			||||||
	IgnoreMessages         string // all protocols
 | 
						IgnoreMessages         string // all protocols
 | 
				
			||||||
	Jid                    string // xmpp
 | 
						Jid                    string // xmpp
 | 
				
			||||||
 | 
						Label                  string // all protocols
 | 
				
			||||||
	Login                  string // mattermost, matrix
 | 
						Login                  string // mattermost, matrix
 | 
				
			||||||
	MediaDownloadSize      int    // all protocols
 | 
						MediaDownloadSize      int    // all protocols
 | 
				
			||||||
	MediaServerDownload    string
 | 
						MediaServerDownload    string
 | 
				
			||||||
@@ -77,6 +87,7 @@ type Protocol struct {
 | 
				
			|||||||
	NickServPassword       string     // IRC
 | 
						NickServPassword       string     // IRC
 | 
				
			||||||
	NicksPerRow            int        // mattermost, slack
 | 
						NicksPerRow            int        // mattermost, slack
 | 
				
			||||||
	NoHomeServerSuffix     bool       // matrix
 | 
						NoHomeServerSuffix     bool       // matrix
 | 
				
			||||||
 | 
						NoSendJoinPart         bool       // all protocols
 | 
				
			||||||
	NoTLS                  bool       // mattermost
 | 
						NoTLS                  bool       // mattermost
 | 
				
			||||||
	Password               string     // IRC,mattermost,XMPP,matrix
 | 
						Password               string     // IRC,mattermost,XMPP,matrix
 | 
				
			||||||
	PrefixMessagesWithNick bool       // mattemost, slack
 | 
						PrefixMessagesWithNick bool       // mattemost, slack
 | 
				
			||||||
@@ -87,6 +98,7 @@ type Protocol struct {
 | 
				
			|||||||
	RemoteNickFormat       string     // all protocols
 | 
						RemoteNickFormat       string     // all protocols
 | 
				
			||||||
	Server                 string     // IRC,mattermost,XMPP,discord
 | 
						Server                 string     // IRC,mattermost,XMPP,discord
 | 
				
			||||||
	ShowJoinPart           bool       // all protocols
 | 
						ShowJoinPart           bool       // all protocols
 | 
				
			||||||
 | 
						ShowTopicChange        bool       // slack
 | 
				
			||||||
	ShowEmbeds             bool       // discord
 | 
						ShowEmbeds             bool       // discord
 | 
				
			||||||
	SkipTLSVerify          bool       // IRC, mattermost
 | 
						SkipTLSVerify          bool       // IRC, mattermost
 | 
				
			||||||
	StripNick              bool       // all protocols
 | 
						StripNick              bool       // all protocols
 | 
				
			||||||
@@ -131,9 +143,9 @@ type SameChannelGateway struct {
 | 
				
			|||||||
	Accounts []string
 | 
						Accounts []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config struct {
 | 
					type ConfigValues struct {
 | 
				
			||||||
	Api                map[string]Protocol
 | 
						Api                map[string]Protocol
 | 
				
			||||||
	IRC                map[string]Protocol
 | 
						Irc                map[string]Protocol
 | 
				
			||||||
	Mattermost         map[string]Protocol
 | 
						Mattermost         map[string]Protocol
 | 
				
			||||||
	Matrix             map[string]Protocol
 | 
						Matrix             map[string]Protocol
 | 
				
			||||||
	Slack              map[string]Protocol
 | 
						Slack              map[string]Protocol
 | 
				
			||||||
@@ -149,85 +161,95 @@ type Config struct {
 | 
				
			|||||||
	SameChannelGateway []SameChannelGateway
 | 
						SameChannelGateway []SameChannelGateway
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BridgeConfig struct {
 | 
					type Config struct {
 | 
				
			||||||
	Config  Protocol
 | 
						v *viper.Viper
 | 
				
			||||||
	General *Protocol
 | 
						*ConfigValues
 | 
				
			||||||
	Account string
 | 
						sync.RWMutex
 | 
				
			||||||
	Remote  chan Message
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewConfig(cfgfile string) *Config {
 | 
					func NewConfig(cfgfile string) *Config {
 | 
				
			||||||
	var cfg Config
 | 
						var cfg ConfigValues
 | 
				
			||||||
	if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil {
 | 
						viper.SetConfigType("toml")
 | 
				
			||||||
 | 
						viper.SetEnvPrefix("matterbridge")
 | 
				
			||||||
 | 
						viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
 | 
				
			||||||
 | 
						viper.AutomaticEnv()
 | 
				
			||||||
 | 
						f, err := os.Open(cfgfile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							log.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fail := false
 | 
						err = viper.ReadConfig(f)
 | 
				
			||||||
	for k, v := range cfg.Mattermost {
 | 
						if err != nil {
 | 
				
			||||||
		res := Deprecated(v, "mattermost."+k)
 | 
							log.Fatal(err)
 | 
				
			||||||
		if res {
 | 
					 | 
				
			||||||
			fail = res
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for k, v := range cfg.Slack {
 | 
						err = viper.Unmarshal(&cfg)
 | 
				
			||||||
		res := Deprecated(v, "slack."+k)
 | 
						if err != nil {
 | 
				
			||||||
		if res {
 | 
							log.Fatal("blah", err)
 | 
				
			||||||
			fail = res
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, v := range cfg.Rocketchat {
 | 
					 | 
				
			||||||
		res := Deprecated(v, "rocketchat."+k)
 | 
					 | 
				
			||||||
		if res {
 | 
					 | 
				
			||||||
			fail = res
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if fail {
 | 
					 | 
				
			||||||
		log.Fatalf("Fix your config. Please see changelog for more information")
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						mycfg := new(Config)
 | 
				
			||||||
 | 
						mycfg.v = viper.GetViper()
 | 
				
			||||||
	if cfg.General.MediaDownloadSize == 0 {
 | 
						if cfg.General.MediaDownloadSize == 0 {
 | 
				
			||||||
		cfg.General.MediaDownloadSize = 1000000
 | 
							cfg.General.MediaDownloadSize = 1000000
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &cfg
 | 
						mycfg.ConfigValues = &cfg
 | 
				
			||||||
 | 
						return mycfg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func OverrideCfgFromEnv(cfg *Config, protocol string, account string) {
 | 
					func NewConfigFromString(input []byte) *Config {
 | 
				
			||||||
	var protoCfg Protocol
 | 
						var cfg ConfigValues
 | 
				
			||||||
	val := reflect.ValueOf(cfg).Elem()
 | 
						viper.SetConfigType("toml")
 | 
				
			||||||
	// loop over the Config struct
 | 
						err := viper.ReadConfig(bytes.NewBuffer(input))
 | 
				
			||||||
	for i := 0; i < val.NumField(); i++ {
 | 
						if err != nil {
 | 
				
			||||||
		typeField := val.Type().Field(i)
 | 
							log.Fatal(err)
 | 
				
			||||||
		// look for the protocol map (both lowercase)
 | 
					 | 
				
			||||||
		if strings.ToLower(typeField.Name) == protocol {
 | 
					 | 
				
			||||||
			// get the Protocol struct from the map
 | 
					 | 
				
			||||||
			data := val.Field(i).MapIndex(reflect.ValueOf(account))
 | 
					 | 
				
			||||||
			protoCfg = data.Interface().(Protocol)
 | 
					 | 
				
			||||||
			protoStruct := reflect.ValueOf(&protoCfg).Elem()
 | 
					 | 
				
			||||||
			// loop over the found protocol struct
 | 
					 | 
				
			||||||
			for i := 0; i < protoStruct.NumField(); i++ {
 | 
					 | 
				
			||||||
				typeField := protoStruct.Type().Field(i)
 | 
					 | 
				
			||||||
				// build our environment key (eg MATTERBRIDGE_MATTERMOST_WORK_LOGIN)
 | 
					 | 
				
			||||||
				key := "matterbridge_" + protocol + "_" + account + "_" + typeField.Name
 | 
					 | 
				
			||||||
				key = strings.ToUpper(key)
 | 
					 | 
				
			||||||
				// search the environment
 | 
					 | 
				
			||||||
				res := os.Getenv(key)
 | 
					 | 
				
			||||||
				// if it exists and the current field is a string
 | 
					 | 
				
			||||||
				// then update the current field
 | 
					 | 
				
			||||||
				if res != "" {
 | 
					 | 
				
			||||||
					fieldVal := protoStruct.Field(i)
 | 
					 | 
				
			||||||
					if fieldVal.Kind() == reflect.String {
 | 
					 | 
				
			||||||
						log.Printf("config: overriding %s from env with %s\n", key, res)
 | 
					 | 
				
			||||||
						fieldVal.Set(reflect.ValueOf(res))
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// update the map with the modified Protocol (cfg.Protocol[account] = Protocol)
 | 
					 | 
				
			||||||
			val.Field(i).SetMapIndex(reflect.ValueOf(account), reflect.ValueOf(protoCfg))
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						err = viper.Unmarshal(&cfg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mycfg := new(Config)
 | 
				
			||||||
 | 
						mycfg.v = viper.GetViper()
 | 
				
			||||||
 | 
						mycfg.ConfigValues = &cfg
 | 
				
			||||||
 | 
						return mycfg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetIconURL(msg *Message, cfg *Protocol) string {
 | 
					func (c *Config) GetBool(key string) bool {
 | 
				
			||||||
	iconURL := cfg.IconURL
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
						//	log.Debugf("getting bool %s = %#v", key, c.v.GetBool(key))
 | 
				
			||||||
 | 
						return c.v.GetBool(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) GetInt(key string) int {
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
						//	log.Debugf("getting int %s = %d", key, c.v.GetInt(key))
 | 
				
			||||||
 | 
						return c.v.GetInt(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) GetString(key string) string {
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
						//	log.Debugf("getting String %s = %s", key, c.v.GetString(key))
 | 
				
			||||||
 | 
						return c.v.GetString(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) GetStringSlice(key string) []string {
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
						// log.Debugf("getting StringSlice %s = %#v", key, c.v.GetStringSlice(key))
 | 
				
			||||||
 | 
						return c.v.GetStringSlice(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) GetStringSlice2D(key string) [][]string {
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
						if res, ok := c.v.Get(key).([][]string); ok {
 | 
				
			||||||
 | 
							return res
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// log.Debugf("getting StringSlice2D %s = %#v", key, c.v.Get(key))
 | 
				
			||||||
 | 
						return [][]string{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetIconURL(msg *Message, iconURL string) string {
 | 
				
			||||||
	info := strings.Split(msg.Account, ".")
 | 
						info := strings.Split(msg.Account, ".")
 | 
				
			||||||
	protocol := info[0]
 | 
						protocol := info[0]
 | 
				
			||||||
	name := info[1]
 | 
						name := info[1]
 | 
				
			||||||
@@ -236,17 +258,3 @@ func GetIconURL(msg *Message, cfg *Protocol) string {
 | 
				
			|||||||
	iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
 | 
						iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
 | 
				
			||||||
	return iconURL
 | 
						return iconURL
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func Deprecated(cfg Protocol, account string) bool {
 | 
					 | 
				
			||||||
	if cfg.BindAddress != "" {
 | 
					 | 
				
			||||||
		log.Printf("ERROR: %s BindAddress is deprecated, you need to change it to WebhookBindAddress.", account)
 | 
					 | 
				
			||||||
	} else if cfg.URL != "" {
 | 
					 | 
				
			||||||
		log.Printf("ERROR: %s URL is deprecated, you need to change it to WebhookURL.", account)
 | 
					 | 
				
			||||||
	} else if cfg.UseAPI {
 | 
					 | 
				
			||||||
		log.Printf("ERROR: %s UseAPI is deprecated, it's enabled by default, please remove it from your config file.", account)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
	//log.Fatalf("ERROR: Fix your config: %s", account)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,15 +2,17 @@ package bdiscord
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/bwmarrin/discordgo"
 | 
						"github.com/bwmarrin/discordgo"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type bdiscord struct {
 | 
					type Bdiscord struct {
 | 
				
			||||||
	c              *discordgo.Session
 | 
						c              *discordgo.Session
 | 
				
			||||||
	Channels       []*discordgo.Channel
 | 
						Channels       []*discordgo.Channel
 | 
				
			||||||
	Nick           string
 | 
						Nick           string
 | 
				
			||||||
@@ -21,70 +23,59 @@ type bdiscord struct {
 | 
				
			|||||||
	webhookToken   string
 | 
						webhookToken   string
 | 
				
			||||||
	channelInfoMap map[string]*config.ChannelInfo
 | 
						channelInfoMap map[string]*config.ChannelInfo
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "discord"
 | 
						b := &Bdiscord{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *bdiscord {
 | 
					 | 
				
			||||||
	b := &bdiscord{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
	b.userMemberMap = make(map[string]*discordgo.Member)
 | 
						b.userMemberMap = make(map[string]*discordgo.Member)
 | 
				
			||||||
	b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
						b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		flog.Debug("Configuring Discord Incoming Webhook")
 | 
							b.Log.Debug("Configuring Discord Incoming Webhook")
 | 
				
			||||||
		b.webhookID, b.webhookToken = b.splitURL(b.Config.WebhookURL)
 | 
							b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) Connect() error {
 | 
					func (b *Bdiscord) Connect() error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	flog.Info("Connecting")
 | 
						var token string
 | 
				
			||||||
	if b.Config.WebhookURL == "" {
 | 
						b.Log.Info("Connecting")
 | 
				
			||||||
		flog.Info("Connecting using token")
 | 
						if b.GetString("WebhookURL") == "" {
 | 
				
			||||||
 | 
							b.Log.Info("Connecting using token")
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		flog.Info("Connecting using webhookurl (for posting) and token")
 | 
							b.Log.Info("Connecting using webhookurl (for posting) and token")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !strings.HasPrefix(b.Config.Token, "Bot ") {
 | 
						if !strings.HasPrefix(b.GetString("Token"), "Bot ") {
 | 
				
			||||||
		b.Config.Token = "Bot " + b.Config.Token
 | 
							token = "Bot " + b.GetString("Token")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.c, err = discordgo.New(b.Config.Token)
 | 
						b.c, err = discordgo.New(token)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	b.c.AddHandler(b.messageCreate)
 | 
						b.c.AddHandler(b.messageCreate)
 | 
				
			||||||
	b.c.AddHandler(b.memberUpdate)
 | 
						b.c.AddHandler(b.memberUpdate)
 | 
				
			||||||
	b.c.AddHandler(b.messageUpdate)
 | 
						b.c.AddHandler(b.messageUpdate)
 | 
				
			||||||
	b.c.AddHandler(b.messageDelete)
 | 
						b.c.AddHandler(b.messageDelete)
 | 
				
			||||||
	err = b.c.Open()
 | 
						err = b.c.Open()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	guilds, err := b.c.UserGuilds(100, "", "")
 | 
						guilds, err := b.c.UserGuilds(100, "", "")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	userinfo, err := b.c.User("@me")
 | 
						userinfo, err := b.c.User("@me")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.Nick = userinfo.Username
 | 
						b.Nick = userinfo.Username
 | 
				
			||||||
	for _, guild := range guilds {
 | 
						for _, guild := range guilds {
 | 
				
			||||||
		if guild.Name == b.Config.Server {
 | 
							if guild.Name == b.GetString("Server") {
 | 
				
			||||||
			b.Channels, err = b.c.GuildChannels(guild.ID)
 | 
								b.Channels, err = b.c.GuildChannels(guild.ID)
 | 
				
			||||||
			b.guildID = guild.ID
 | 
								b.guildID = guild.ID
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -92,11 +83,11 @@ func (b *bdiscord) Connect() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) Disconnect() error {
 | 
					func (b *Bdiscord) Disconnect() error {
 | 
				
			||||||
	return nil
 | 
						return b.c.Close()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
	b.channelInfoMap[channel.ID] = &channel
 | 
						b.channelInfoMap[channel.ID] = &channel
 | 
				
			||||||
	idcheck := strings.Split(channel.Name, "ID:")
 | 
						idcheck := strings.Split(channel.Name, "ID:")
 | 
				
			||||||
	if len(idcheck) > 1 {
 | 
						if len(idcheck) > 1 {
 | 
				
			||||||
@@ -105,99 +96,111 @@ func (b *bdiscord) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) Send(msg config.Message) (string, error) {
 | 
					func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	channelID := b.getChannelID(msg.Channel)
 | 
						channelID := b.getChannelID(msg.Channel)
 | 
				
			||||||
	if channelID == "" {
 | 
						if channelID == "" {
 | 
				
			||||||
		flog.Errorf("Could not find channelID for %v", msg.Channel)
 | 
							return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
 | 
				
			||||||
		return "", nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a action /me of the message
 | 
				
			||||||
	if msg.Event == config.EVENT_USER_ACTION {
 | 
						if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
		msg.Text = "_" + msg.Text + "_"
 | 
							msg.Text = "_" + msg.Text + "_"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// use initial webhook
 | 
				
			||||||
	wID := b.webhookID
 | 
						wID := b.webhookID
 | 
				
			||||||
	wToken := b.webhookToken
 | 
						wToken := b.webhookToken
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if have a channel specific webhook
 | 
				
			||||||
	if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok {
 | 
						if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok {
 | 
				
			||||||
		if ci.Options.WebhookURL != "" {
 | 
							if ci.Options.WebhookURL != "" {
 | 
				
			||||||
			wID, wToken = b.splitURL(ci.Options.WebhookURL)
 | 
								wID, wToken = b.splitURL(ci.Options.WebhookURL)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if wID == "" {
 | 
						// Use webhook to send the message
 | 
				
			||||||
		flog.Debugf("Broadcasting using token (API)")
 | 
						if wID != "" {
 | 
				
			||||||
		if msg.Event == config.EVENT_MSG_DELETE {
 | 
							// skip events
 | 
				
			||||||
			if msg.ID == "" {
 | 
							if msg.Event != "" {
 | 
				
			||||||
				return "", nil
 | 
								return "", nil
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
					 | 
				
			||||||
			return "", err
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if msg.ID != "" {
 | 
							b.Log.Debugf("Broadcasting using Webhook")
 | 
				
			||||||
			_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)
 | 
							err := b.c.WebhookExecute(
 | 
				
			||||||
			return msg.ID, err
 | 
								wID,
 | 
				
			||||||
		}
 | 
								wToken,
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
		if msg.Extra != nil {
 | 
								&discordgo.WebhookParams{
 | 
				
			||||||
			// check if we have files to upload (from slack, telegram or mattermost)
 | 
									Content:   msg.Text,
 | 
				
			||||||
			if len(msg.Extra["file"]) > 0 {
 | 
									Username:  msg.Username,
 | 
				
			||||||
				var err error
 | 
									AvatarURL: msg.Avatar,
 | 
				
			||||||
				for _, f := range msg.Extra["file"] {
 | 
								})
 | 
				
			||||||
					fi := f.(config.FileInfo)
 | 
							return "", err
 | 
				
			||||||
					files := []*discordgo.File{}
 | 
					 | 
				
			||||||
					files = append(files, &discordgo.File{fi.Name, "", bytes.NewReader(*fi.Data)})
 | 
					 | 
				
			||||||
					_, err = b.c.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{Content: msg.Username + fi.Comment, Files: files})
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						flog.Errorf("file upload failed: %#v", err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return "", nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		res, err := b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return "", err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return res.ID, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Broadcasting using Webhook")
 | 
					
 | 
				
			||||||
	err := b.c.WebhookExecute(
 | 
						b.Log.Debugf("Broadcasting using token (API)")
 | 
				
			||||||
		wID,
 | 
					
 | 
				
			||||||
		wToken,
 | 
						// Delete message
 | 
				
			||||||
		true,
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		&discordgo.WebhookParams{
 | 
							if msg.ID == "" {
 | 
				
			||||||
			Content:   msg.Text,
 | 
								return "", nil
 | 
				
			||||||
			Username:  msg.Username,
 | 
							}
 | 
				
			||||||
			AvatarURL: msg.Avatar,
 | 
							err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
				
			||||||
		})
 | 
							return "", err
 | 
				
			||||||
	return "", err
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Upload a file if it exists
 | 
				
			||||||
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// check if we have files to upload (from slack, telegram or mattermost)
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								return b.handleUploadFile(&msg, channelID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Edit message
 | 
				
			||||||
 | 
						if msg.ID != "" {
 | 
				
			||||||
 | 
							_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)
 | 
				
			||||||
 | 
							return msg.ID, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Post normal message
 | 
				
			||||||
 | 
						res, err := b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res.ID, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) {
 | 
					func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) {
 | 
				
			||||||
	rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EVENT_MSG_DELETE, Text: config.EVENT_MSG_DELETE}
 | 
						rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EVENT_MSG_DELETE, Text: config.EVENT_MSG_DELETE}
 | 
				
			||||||
	rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
						rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
				
			||||||
	if b.UseChannelID {
 | 
						if b.UseChannelID {
 | 
				
			||||||
		rmsg.Channel = "ID:" + m.ChannelID
 | 
							rmsg.Channel = "ID:" + m.ChannelID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Sending message from %s to gateway", b.Account)
 | 
						b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
 | 
				
			||||||
	flog.Debugf("Message is %#v", rmsg)
 | 
						b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
	b.Remote <- rmsg
 | 
						b.Remote <- rmsg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
 | 
					func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
 | 
				
			||||||
	if b.Config.EditDisable {
 | 
						if b.GetBool("EditDisable") {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// only when message is actually edited
 | 
						// only when message is actually edited
 | 
				
			||||||
	if m.Message.EditedTimestamp != "" {
 | 
						if m.Message.EditedTimestamp != "" {
 | 
				
			||||||
		flog.Debugf("Sending edit message")
 | 
							b.Log.Debugf("Sending edit message")
 | 
				
			||||||
		m.Content = m.Content + b.Config.EditSuffix
 | 
							m.Content = m.Content + b.GetString("EditSuffix")
 | 
				
			||||||
		b.messageCreate(s, (*discordgo.MessageCreate)(m))
 | 
							b.messageCreate(s, (*discordgo.MessageCreate)(m))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 | 
					func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// not relay our own messages
 | 
						// not relay our own messages
 | 
				
			||||||
	if m.Author.Username == b.Nick {
 | 
						if m.Author.Username == b.Nick {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -207,69 +210,73 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// add the url of the attachments to content
 | 
				
			||||||
	if len(m.Attachments) > 0 {
 | 
						if len(m.Attachments) > 0 {
 | 
				
			||||||
		for _, attach := range m.Attachments {
 | 
							for _, attach := range m.Attachments {
 | 
				
			||||||
			m.Content = m.Content + "\n" + attach.URL
 | 
								m.Content = m.Content + "\n" + attach.URL
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var text string
 | 
						rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", UserID: m.Author.ID, ID: m.ID}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.Content != "" {
 | 
						if m.Content != "" {
 | 
				
			||||||
		flog.Debugf("Receiving message %#v", m.Message)
 | 
							b.Log.Debugf("== Receiving event %#v", m.Message)
 | 
				
			||||||
		if len(m.MentionRoles) > 0 {
 | 
					 | 
				
			||||||
			m.Message.Content = b.replaceRoleMentions(m.Message.Content)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		m.Message.Content = b.stripCustomoji(m.Message.Content)
 | 
							m.Message.Content = b.stripCustomoji(m.Message.Content)
 | 
				
			||||||
		m.Message.Content = b.replaceChannelMentions(m.Message.Content)
 | 
							m.Message.Content = b.replaceChannelMentions(m.Message.Content)
 | 
				
			||||||
		text = m.ContentWithMentionsReplaced()
 | 
							rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("ContentWithMoreMentionsReplaced failed: %s", err)
 | 
				
			||||||
 | 
								rmsg.Text = m.ContentWithMentionsReplaced()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg",
 | 
						// set channel name
 | 
				
			||||||
		UserID: m.Author.ID, ID: m.ID}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
						rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
				
			||||||
	if b.UseChannelID {
 | 
						if b.UseChannelID {
 | 
				
			||||||
		rmsg.Channel = "ID:" + m.ChannelID
 | 
							rmsg.Channel = "ID:" + m.ChannelID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !b.Config.UseUserName {
 | 
						// set username
 | 
				
			||||||
 | 
						if !b.GetBool("UseUserName") {
 | 
				
			||||||
		rmsg.Username = b.getNick(m.Author)
 | 
							rmsg.Username = b.getNick(m.Author)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		rmsg.Username = m.Author.Username
 | 
							rmsg.Username = m.Author.Username
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if b.Config.ShowEmbeds && m.Message.Embeds != nil {
 | 
						// if we have embedded content add it to text
 | 
				
			||||||
 | 
						if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil {
 | 
				
			||||||
		for _, embed := range m.Message.Embeds {
 | 
							for _, embed := range m.Message.Embeds {
 | 
				
			||||||
			text = text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n"
 | 
								rmsg.Text = rmsg.Text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// no empty messages
 | 
						// no empty messages
 | 
				
			||||||
	if text == "" {
 | 
						if rmsg.Text == "" {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	text, ok := b.replaceAction(text)
 | 
						// do we have a /me action
 | 
				
			||||||
 | 
						var ok bool
 | 
				
			||||||
 | 
						rmsg.Text, ok = b.replaceAction(rmsg.Text)
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
		rmsg.Event = config.EVENT_USER_ACTION
 | 
							rmsg.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rmsg.Text = text
 | 
						b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account)
 | 
				
			||||||
	flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.Account)
 | 
						b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
	flog.Debugf("Message is %#v", rmsg)
 | 
					 | 
				
			||||||
	b.Remote <- rmsg
 | 
						b.Remote <- rmsg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
 | 
					func (b *Bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
 | 
				
			||||||
	b.Lock()
 | 
						b.Lock()
 | 
				
			||||||
	if _, ok := b.userMemberMap[m.Member.User.ID]; ok {
 | 
						if _, ok := b.userMemberMap[m.Member.User.ID]; ok {
 | 
				
			||||||
		flog.Debugf("%s: memberupdate: user %s (nick %s) changes nick to %s", b.Account, m.Member.User.Username, b.userMemberMap[m.Member.User.ID].Nick, m.Member.Nick)
 | 
							b.Log.Debugf("%s: memberupdate: user %s (nick %s) changes nick to %s", b.Account, m.Member.User.Username, b.userMemberMap[m.Member.User.ID].Nick, m.Member.Nick)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.userMemberMap[m.Member.User.ID] = m.Member
 | 
						b.userMemberMap[m.Member.User.ID] = m.Member
 | 
				
			||||||
	b.Unlock()
 | 
						b.Unlock()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) getNick(user *discordgo.User) string {
 | 
					func (b *Bdiscord) getNick(user *discordgo.User) string {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	b.Lock()
 | 
						b.Lock()
 | 
				
			||||||
	defer b.Unlock()
 | 
						defer b.Unlock()
 | 
				
			||||||
@@ -296,7 +303,7 @@ func (b *bdiscord) getNick(user *discordgo.User) string {
 | 
				
			|||||||
	return user.Username
 | 
						return user.Username
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) getChannelID(name string) string {
 | 
					func (b *Bdiscord) getChannelID(name string) string {
 | 
				
			||||||
	idcheck := strings.Split(name, "ID:")
 | 
						idcheck := strings.Split(name, "ID:")
 | 
				
			||||||
	if len(idcheck) > 1 {
 | 
						if len(idcheck) > 1 {
 | 
				
			||||||
		return idcheck[1]
 | 
							return idcheck[1]
 | 
				
			||||||
@@ -309,7 +316,7 @@ func (b *bdiscord) getChannelID(name string) string {
 | 
				
			|||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) getChannelName(id string) string {
 | 
					func (b *Bdiscord) getChannelName(id string) string {
 | 
				
			||||||
	for _, channel := range b.Channels {
 | 
						for _, channel := range b.Channels {
 | 
				
			||||||
		if channel.ID == id {
 | 
							if channel.ID == id {
 | 
				
			||||||
			return channel.Name
 | 
								return channel.Name
 | 
				
			||||||
@@ -318,19 +325,7 @@ func (b *bdiscord) getChannelName(id string) string {
 | 
				
			|||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) replaceRoleMentions(text string) string {
 | 
					func (b *Bdiscord) replaceChannelMentions(text string) string {
 | 
				
			||||||
	roles, err := b.c.GuildRoles(b.guildID)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		flog.Debugf("%#v", string(err.(*discordgo.RESTError).ResponseBody))
 | 
					 | 
				
			||||||
		return text
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, role := range roles {
 | 
					 | 
				
			||||||
		text = strings.Replace(text, "<@&"+role.ID+">", "@"+role.Name, -1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return text
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (b *bdiscord) replaceChannelMentions(text string) string {
 | 
					 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	re := regexp.MustCompile("<#[0-9]+>")
 | 
						re := regexp.MustCompile("<#[0-9]+>")
 | 
				
			||||||
	text = re.ReplaceAllStringFunc(text, func(m string) string {
 | 
						text = re.ReplaceAllStringFunc(text, func(m string) string {
 | 
				
			||||||
@@ -349,28 +344,31 @@ func (b *bdiscord) replaceChannelMentions(text string) string {
 | 
				
			|||||||
	return text
 | 
						return text
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) replaceAction(text string) (string, bool) {
 | 
					func (b *Bdiscord) replaceAction(text string) (string, bool) {
 | 
				
			||||||
	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") {
 | 
						if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") {
 | 
				
			||||||
		return strings.Replace(text, "_", "", -1), true
 | 
							return strings.Replace(text, "_", "", -1), true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return text, false
 | 
						return text, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) stripCustomoji(text string) string {
 | 
					func (b *Bdiscord) stripCustomoji(text string) string {
 | 
				
			||||||
	// <:doge:302803592035958784>
 | 
						// <:doge:302803592035958784>
 | 
				
			||||||
	re := regexp.MustCompile("<(:.*?:)[0-9]+>")
 | 
						re := regexp.MustCompile("<(:.*?:)[0-9]+>")
 | 
				
			||||||
	return re.ReplaceAllString(text, `$1`)
 | 
						return re.ReplaceAllString(text, `$1`)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// splitURL splits a webhookURL and returns the id and token
 | 
					// splitURL splits a webhookURL and returns the id and token
 | 
				
			||||||
func (b *bdiscord) splitURL(url string) (string, string) {
 | 
					func (b *Bdiscord) splitURL(url string) (string, string) {
 | 
				
			||||||
	webhookURLSplit := strings.Split(url, "/")
 | 
						webhookURLSplit := strings.Split(url, "/")
 | 
				
			||||||
 | 
						if len(webhookURLSplit) != 7 {
 | 
				
			||||||
 | 
							b.Log.Fatalf("%s is no correct discord WebhookURL", url)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return webhookURLSplit[len(webhookURLSplit)-2], webhookURLSplit[len(webhookURLSplit)-1]
 | 
						return webhookURLSplit[len(webhookURLSplit)-2], webhookURLSplit[len(webhookURLSplit)-1]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// useWebhook returns true if we have a webhook defined somewhere
 | 
					// useWebhook returns true if we have a webhook defined somewhere
 | 
				
			||||||
func (b *bdiscord) useWebhook() bool {
 | 
					func (b *Bdiscord) useWebhook() bool {
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, channel := range b.channelInfoMap {
 | 
						for _, channel := range b.channelInfoMap {
 | 
				
			||||||
@@ -382,9 +380,9 @@ func (b *bdiscord) useWebhook() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// isWebhookID returns true if the specified id is used in a defined webhook
 | 
					// isWebhookID returns true if the specified id is used in a defined webhook
 | 
				
			||||||
func (b *bdiscord) isWebhookID(id string) bool {
 | 
					func (b *Bdiscord) isWebhookID(id string) bool {
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		wID, _ := b.splitURL(b.Config.WebhookURL)
 | 
							wID, _ := b.splitURL(b.GetString("WebhookURL"))
 | 
				
			||||||
		if wID == id {
 | 
							if wID == id {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -399,3 +397,18 @@ func (b *bdiscord) isWebhookID(id string) bool {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleUploadFile handles native upload of files
 | 
				
			||||||
 | 
					func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							files := []*discordgo.File{}
 | 
				
			||||||
 | 
							files = append(files, &discordgo.File{fi.Name, "", bytes.NewReader(*fi.Data)})
 | 
				
			||||||
 | 
							_, err = b.c.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{Content: msg.Username + fi.Comment, Files: files})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", fmt.Errorf("file upload failed: %#v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,9 @@ package bgitter
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/42wim/go-gitter"
 | 
						"github.com/42wim/go-gitter"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -13,31 +14,26 @@ type Bgitter struct {
 | 
				
			|||||||
	User  *gitter.User
 | 
						User  *gitter.User
 | 
				
			||||||
	Users []gitter.User
 | 
						Users []gitter.User
 | 
				
			||||||
	Rooms []gitter.Room
 | 
						Rooms []gitter.Room
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "gitter"
 | 
						return &Bgitter{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bgitter {
 | 
					 | 
				
			||||||
	return &Bgitter{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bgitter) Connect() error {
 | 
					func (b *Bgitter) Connect() error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	flog.Info("Connecting")
 | 
						b.Log.Info("Connecting")
 | 
				
			||||||
	b.c = gitter.New(b.Config.Token)
 | 
						b.c = gitter.New(b.GetString("Token"))
 | 
				
			||||||
	b.User, err = b.c.GetUser()
 | 
						b.User, err = b.c.GetUser()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Rooms, err = b.c.GetRooms()
 | 
				
			||||||
	b.Rooms, _ = b.c.GetRooms()
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,8 +69,9 @@ func (b *Bgitter) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
		for event := range stream.Event {
 | 
							for event := range stream.Event {
 | 
				
			||||||
			switch ev := event.Data.(type) {
 | 
								switch ev := event.Data.(type) {
 | 
				
			||||||
			case *gitter.MessageReceived:
 | 
								case *gitter.MessageReceived:
 | 
				
			||||||
 | 
									// ignore message sent from ourselves
 | 
				
			||||||
				if ev.Message.From.ID != b.User.ID {
 | 
									if ev.Message.From.ID != b.User.ID {
 | 
				
			||||||
					flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account)
 | 
										b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account)
 | 
				
			||||||
					rmsg := config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room,
 | 
										rmsg := config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room,
 | 
				
			||||||
						Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username), UserID: ev.Message.From.ID,
 | 
											Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username), UserID: ev.Message.From.ID,
 | 
				
			||||||
						ID: ev.Message.ID}
 | 
											ID: ev.Message.ID}
 | 
				
			||||||
@@ -82,11 +79,11 @@ func (b *Bgitter) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
						rmsg.Event = config.EVENT_USER_ACTION
 | 
											rmsg.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
						rmsg.Text = strings.Replace(rmsg.Text, "@"+ev.Message.From.Username+" ", "", -1)
 | 
											rmsg.Text = strings.Replace(rmsg.Text, "@"+ev.Message.From.Username+" ", "", -1)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					flog.Debugf("Message is %#v", rmsg)
 | 
										b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
					b.Remote <- rmsg
 | 
										b.Remote <- rmsg
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case *gitter.GitterConnectionClosed:
 | 
								case *gitter.GitterConnectionClosed:
 | 
				
			||||||
				flog.Errorf("connection with gitter closed for room %s", room)
 | 
									b.Log.Errorf("connection with gitter closed for room %s", room)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}(stream, room.URI)
 | 
						}(stream, room.URI)
 | 
				
			||||||
@@ -94,25 +91,39 @@ func (b *Bgitter) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bgitter) Send(msg config.Message) (string, error) {
 | 
					func (b *Bgitter) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
	roomID := b.getRoomID(msg.Channel)
 | 
						roomID := b.getRoomID(msg.Channel)
 | 
				
			||||||
	if roomID == "" {
 | 
						if roomID == "" {
 | 
				
			||||||
		flog.Errorf("Could not find roomID for %v", msg.Channel)
 | 
							b.Log.Errorf("Could not find roomID for %v", msg.Channel)
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete message
 | 
				
			||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		if msg.ID == "" {
 | 
							if msg.ID == "" {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// gitter has no delete message api
 | 
							// gitter has no delete message api so we edit message to ""
 | 
				
			||||||
		_, err := b.c.UpdateMessage(roomID, msg.ID, "")
 | 
							_, err := b.c.UpdateMessage(roomID, msg.ID, "")
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Upload a file (in gitter case send the upload URL because gitter has no native upload support)
 | 
				
			||||||
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.c.SendMessage(roomID, rmsg.Username+rmsg.Text)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								return b.handleUploadFile(&msg, roomID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Edit message
 | 
				
			||||||
	if msg.ID != "" {
 | 
						if msg.ID != "" {
 | 
				
			||||||
		flog.Debugf("updating message with id %s", msg.ID)
 | 
							b.Log.Debugf("updating message with id %s", msg.ID)
 | 
				
			||||||
		_, err := b.c.UpdateMessage(roomID, msg.ID, msg.Username+msg.Text)
 | 
							_, err := b.c.UpdateMessage(roomID, msg.ID, msg.Username+msg.Text)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
@@ -120,22 +131,7 @@ func (b *Bgitter) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						// Post normal message
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
					 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
					 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
					 | 
				
			||||||
				if fi.URL != "" {
 | 
					 | 
				
			||||||
					msg.Text = fi.URL
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				_, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					return "", err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return "", nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
						resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
@@ -163,3 +159,20 @@ func (b *Bgitter) getAvatar(user string) string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return avatar
 | 
						return avatar
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bgitter) handleUploadFile(msg *config.Message, roomID string) (string, error) {
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							if fi.Comment != "" {
 | 
				
			||||||
 | 
								msg.Text += fi.Comment + ": "
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if fi.URL != "" {
 | 
				
			||||||
 | 
								msg.Text = fi.URL
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,28 +2,37 @@ package helper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func DownloadFile(url string) (*[]byte, error) {
 | 
					func DownloadFile(url string) (*[]byte, error) {
 | 
				
			||||||
 | 
						return DownloadFileAuth(url, "")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DownloadFileAuth(url string, auth string) (*[]byte, error) {
 | 
				
			||||||
	var buf bytes.Buffer
 | 
						var buf bytes.Buffer
 | 
				
			||||||
	client := &http.Client{
 | 
						client := &http.Client{
 | 
				
			||||||
		Timeout: time.Second * 5,
 | 
							Timeout: time.Second * 5,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req, err := http.NewRequest("GET", url, nil)
 | 
						req, err := http.NewRequest("GET", url, nil)
 | 
				
			||||||
 | 
						if auth != "" {
 | 
				
			||||||
 | 
							req.Header.Add("Authorization", auth)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	resp, err := client.Do(req)
 | 
						resp, err := client.Do(req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		resp.Body.Close()
 | 
					 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
	io.Copy(&buf, resp.Body)
 | 
						io.Copy(&buf, resp.Body)
 | 
				
			||||||
	data := buf.Bytes()
 | 
						data := buf.Bytes()
 | 
				
			||||||
	resp.Body.Close()
 | 
					 | 
				
			||||||
	return &data, nil
 | 
						return &data, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,3 +47,44 @@ func SplitStringLength(input string, length int) string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return str
 | 
						return str
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handle all the stuff we put into extra
 | 
				
			||||||
 | 
					func HandleExtra(msg *config.Message, general *config.Protocol) []config.Message {
 | 
				
			||||||
 | 
						extra := msg.Extra
 | 
				
			||||||
 | 
						rmsg := []config.Message{}
 | 
				
			||||||
 | 
						if len(extra[config.EVENT_FILE_FAILURE_SIZE]) > 0 {
 | 
				
			||||||
 | 
							for _, f := range extra[config.EVENT_FILE_FAILURE_SIZE] {
 | 
				
			||||||
 | 
								fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
								text := fmt.Sprintf("file %s too big to download (%#v > allowed size: %#v)", fi.Name, fi.Size, general.MediaDownloadSize)
 | 
				
			||||||
 | 
								rmsg = append(rmsg, config.Message{Text: text, Username: "<system> ", Channel: msg.Channel})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return rmsg
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return rmsg
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetAvatar(av map[string]string, userid string, general *config.Protocol) string {
 | 
				
			||||||
 | 
						if sha, ok := av[userid]; ok {
 | 
				
			||||||
 | 
							return general.MediaServerDownload + "/" + sha + "/" + userid + ".png"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func HandleDownloadSize(flog *log.Entry, msg *config.Message, name string, size int64, general *config.Protocol) error {
 | 
				
			||||||
 | 
						flog.Debugf("Trying to download %#v with size %#v", name, size)
 | 
				
			||||||
 | 
						if int(size) > general.MediaDownloadSize {
 | 
				
			||||||
 | 
							msg.Event = config.EVENT_FILE_FAILURE_SIZE
 | 
				
			||||||
 | 
							msg.Extra[msg.Event] = append(msg.Extra[msg.Event], config.FileInfo{Name: name, Comment: msg.Text, Size: size})
 | 
				
			||||||
 | 
							return fmt.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", name, size, general.MediaDownloadSize)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func HandleDownloadData(flog *log.Entry, msg *config.Message, name, comment, url string, data *[]byte, general *config.Protocol) {
 | 
				
			||||||
 | 
						var avatar bool
 | 
				
			||||||
 | 
						flog.Debugf("Download OK %#v %#v", name, len(*data))
 | 
				
			||||||
 | 
						if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
				
			||||||
 | 
							avatar = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: name, Data: data, URL: url, Comment: comment, Avatar: avatar})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,9 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"github.com/lrstanley/girc"
 | 
						"github.com/lrstanley/girc"
 | 
				
			||||||
	"github.com/paulrosania/go-charset/charset"
 | 
						"github.com/paulrosania/go-charset/charset"
 | 
				
			||||||
	_ "github.com/paulrosania/go-charset/data"
 | 
						_ "github.com/paulrosania/go-charset/data"
 | 
				
			||||||
@@ -19,40 +19,41 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
						"unicode/utf8"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Birc struct {
 | 
					type Birc struct {
 | 
				
			||||||
	i               *girc.Client
 | 
						i                                         *girc.Client
 | 
				
			||||||
	Nick            string
 | 
						Nick                                      string
 | 
				
			||||||
	names           map[string][]string
 | 
						names                                     map[string][]string
 | 
				
			||||||
	connected       chan struct{}
 | 
						connected                                 chan struct{}
 | 
				
			||||||
	Local           chan config.Message // local queue for flood control
 | 
						Local                                     chan config.Message // local queue for flood control
 | 
				
			||||||
	FirstConnection bool
 | 
						FirstConnection                           bool
 | 
				
			||||||
 | 
						MessageDelay, MessageQueue, MessageLength int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "irc"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Birc {
 | 
					 | 
				
			||||||
	b := &Birc{}
 | 
						b := &Birc{}
 | 
				
			||||||
	b.BridgeConfig = cfg
 | 
						b.Config = cfg
 | 
				
			||||||
	b.Nick = b.Config.Nick
 | 
						b.Nick = b.GetString("Nick")
 | 
				
			||||||
	b.names = make(map[string][]string)
 | 
						b.names = make(map[string][]string)
 | 
				
			||||||
	b.connected = make(chan struct{})
 | 
						b.connected = make(chan struct{})
 | 
				
			||||||
	if b.Config.MessageDelay == 0 {
 | 
						if b.GetInt("MessageDelay") == 0 {
 | 
				
			||||||
		b.Config.MessageDelay = 1300
 | 
							b.MessageDelay = 1300
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							b.MessageDelay = b.GetInt("MessageDelay")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.MessageQueue == 0 {
 | 
						if b.GetInt("MessageQueue") == 0 {
 | 
				
			||||||
		b.Config.MessageQueue = 30
 | 
							b.MessageQueue = 30
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							b.MessageQueue = b.GetInt("MessageQueue")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.MessageLength == 0 {
 | 
						if b.GetInt("MessageLength") == 0 {
 | 
				
			||||||
		b.Config.MessageLength = 400
 | 
							b.MessageLength = 400
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							b.MessageLength = b.GetInt("MessageLength")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.FirstConnection = true
 | 
						b.FirstConnection = true
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
@@ -69,9 +70,9 @@ func (b *Birc) Command(msg *config.Message) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) Connect() error {
 | 
					func (b *Birc) Connect() error {
 | 
				
			||||||
	b.Local = make(chan config.Message, b.Config.MessageQueue+10)
 | 
						b.Local = make(chan config.Message, b.MessageQueue+10)
 | 
				
			||||||
	flog.Infof("Connecting %s", b.Config.Server)
 | 
						b.Log.Infof("Connecting %s", b.GetString("Server"))
 | 
				
			||||||
	server, portstr, err := net.SplitHostPort(b.Config.Server)
 | 
						server, portstr, err := net.SplitHostPort(b.GetString("Server"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -80,7 +81,7 @@ func (b *Birc) Connect() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// fix strict user handling of girc
 | 
						// fix strict user handling of girc
 | 
				
			||||||
	user := b.Config.Nick
 | 
						user := b.GetString("Nick")
 | 
				
			||||||
	for !girc.IsValidUser(user) {
 | 
						for !girc.IsValidUser(user) {
 | 
				
			||||||
		if len(user) == 1 {
 | 
							if len(user) == 1 {
 | 
				
			||||||
			user = "matterbridge"
 | 
								user = "matterbridge"
 | 
				
			||||||
@@ -91,28 +92,28 @@ func (b *Birc) Connect() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	i := girc.New(girc.Config{
 | 
						i := girc.New(girc.Config{
 | 
				
			||||||
		Server:     server,
 | 
							Server:     server,
 | 
				
			||||||
		ServerPass: b.Config.Password,
 | 
							ServerPass: b.GetString("Password"),
 | 
				
			||||||
		Port:       port,
 | 
							Port:       port,
 | 
				
			||||||
		Nick:       b.Config.Nick,
 | 
							Nick:       b.GetString("Nick"),
 | 
				
			||||||
		User:       user,
 | 
							User:       user,
 | 
				
			||||||
		Name:       b.Config.Nick,
 | 
							Name:       b.GetString("Nick"),
 | 
				
			||||||
		SSL:        b.Config.UseTLS,
 | 
							SSL:        b.GetBool("UseTLS"),
 | 
				
			||||||
		TLSConfig:  &tls.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, ServerName: server},
 | 
							TLSConfig:  &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server},
 | 
				
			||||||
		PingDelay:  time.Minute,
 | 
							PingDelay:  time.Minute,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if b.Config.UseSASL {
 | 
						if b.GetBool("UseSASL") {
 | 
				
			||||||
		i.Config.SASL = &girc.SASLPlain{b.Config.NickServNick, b.Config.NickServPassword}
 | 
							i.Config.SASL = &girc.SASLPlain{b.GetString("NickServNick"), b.GetString("NickServPassword")}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	i.Handlers.Add(girc.RPL_WELCOME, b.handleNewConnection)
 | 
						i.Handlers.Add(girc.RPL_WELCOME, b.handleNewConnection)
 | 
				
			||||||
	i.Handlers.Add(girc.RPL_ENDOFMOTD, b.handleOtherAuth)
 | 
						i.Handlers.Add(girc.RPL_ENDOFMOTD, b.handleOtherAuth)
 | 
				
			||||||
	i.Handlers.Add("*", b.handleOther)
 | 
						i.Handlers.Add(girc.ALL_EVENTS, b.handleOther)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		for {
 | 
							for {
 | 
				
			||||||
			if err := i.Connect(); err != nil {
 | 
								if err := i.Connect(); err != nil {
 | 
				
			||||||
				flog.Errorf("error: %s", err)
 | 
									b.Log.Errorf("error: %s", err)
 | 
				
			||||||
				flog.Info("reconnecting in 30 seconds...")
 | 
									b.Log.Info("reconnecting in 30 seconds...")
 | 
				
			||||||
				time.Sleep(30 * time.Second)
 | 
									time.Sleep(30 * time.Second)
 | 
				
			||||||
				i.Handlers.Clear(girc.RPL_WELCOME)
 | 
									i.Handlers.Clear(girc.RPL_WELCOME)
 | 
				
			||||||
				i.Handlers.Add(girc.RPL_WELCOME, func(client *girc.Client, event girc.Event) {
 | 
									i.Handlers.Add(girc.RPL_WELCOME, func(client *girc.Client, event girc.Event) {
 | 
				
			||||||
@@ -128,25 +129,27 @@ func (b *Birc) Connect() error {
 | 
				
			|||||||
	b.i = i
 | 
						b.i = i
 | 
				
			||||||
	select {
 | 
						select {
 | 
				
			||||||
	case <-b.connected:
 | 
						case <-b.connected:
 | 
				
			||||||
		flog.Info("Connection succeeded")
 | 
							b.Log.Info("Connection succeeded")
 | 
				
			||||||
	case <-time.After(time.Second * 30):
 | 
						case <-time.After(time.Second * 30):
 | 
				
			||||||
		return fmt.Errorf("connection timed out")
 | 
							return fmt.Errorf("connection timed out")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	//i.Debug = false
 | 
						//i.Debug = false
 | 
				
			||||||
	i.Handlers.Clear("*")
 | 
						if b.GetInt("DebugLevel") == 0 {
 | 
				
			||||||
 | 
							i.Handlers.Clear(girc.ALL_EVENTS)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	go b.doSend()
 | 
						go b.doSend()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) Disconnect() error {
 | 
					func (b *Birc) Disconnect() error {
 | 
				
			||||||
	//b.i.Disconnect()
 | 
						b.i.Close()
 | 
				
			||||||
	close(b.Local)
 | 
						close(b.Local)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Birc) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
	if channel.Options.Key != "" {
 | 
						if channel.Options.Key != "" {
 | 
				
			||||||
		flog.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name)
 | 
							b.Log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name)
 | 
				
			||||||
		b.i.Cmd.JoinKey(channel.Name, channel.Options.Key)
 | 
							b.i.Cmd.JoinKey(channel.Name, channel.Options.Key)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		b.i.Cmd.Join(channel.Name)
 | 
							b.i.Cmd.Join(channel.Name)
 | 
				
			||||||
@@ -159,16 +162,25 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
					
 | 
				
			||||||
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// we can be in between reconnects #385
 | 
				
			||||||
 | 
						if !b.i.IsConnected() {
 | 
				
			||||||
 | 
							b.Log.Error("Not connected to server, dropping message")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Execute a command
 | 
				
			||||||
	if strings.HasPrefix(msg.Text, "!") {
 | 
						if strings.HasPrefix(msg.Text, "!") {
 | 
				
			||||||
		b.Command(&msg)
 | 
							b.Command(&msg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if b.Config.Charset != "" {
 | 
						// convert to specified charset
 | 
				
			||||||
 | 
						if b.GetString("Charset") != "" {
 | 
				
			||||||
		buf := new(bytes.Buffer)
 | 
							buf := new(bytes.Buffer)
 | 
				
			||||||
		w, err := charset.NewWriter(b.Config.Charset, buf)
 | 
							w, err := charset.NewWriter(b.GetString("Charset"), buf)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			flog.Errorf("charset from utf-8 conversion failed: %s", err)
 | 
								b.Log.Errorf("charset from utf-8 conversion failed: %s", err)
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Fprintf(w, msg.Text)
 | 
							fmt.Fprintf(w, msg.Text)
 | 
				
			||||||
@@ -176,10 +188,17 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		msg.Text = buf.String()
 | 
							msg.Text = buf.String()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle files
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.Local <- rmsg
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
								for _, f := range msg.Extra["file"] {
 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
									fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
									if fi.Comment != "" {
 | 
				
			||||||
 | 
										msg.Text += fi.Comment + ": "
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				if fi.URL != "" {
 | 
									if fi.URL != "" {
 | 
				
			||||||
					msg.Text = fi.URL
 | 
										msg.Text = fi.URL
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -190,34 +209,38 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// split long messages on messageLength, to avoid clipped messages #281
 | 
						// split long messages on messageLength, to avoid clipped messages #281
 | 
				
			||||||
	if b.Config.MessageSplit {
 | 
						if b.GetBool("MessageSplit") {
 | 
				
			||||||
		msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength)
 | 
							msg.Text = helper.SplitStringLength(msg.Text, b.MessageLength)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, text := range strings.Split(msg.Text, "\n") {
 | 
						for _, text := range strings.Split(msg.Text, "\n") {
 | 
				
			||||||
		input := []rune(text)
 | 
							if len(text) > b.MessageLength {
 | 
				
			||||||
		if len(text) > b.Config.MessageLength {
 | 
								text = text[:b.MessageLength-len(" <message clipped>")]
 | 
				
			||||||
			text = string(input[:b.Config.MessageLength]) + " <message clipped>"
 | 
								if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
 | 
				
			||||||
 | 
									text = text[:len(text)-size]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								text += " <message clipped>"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(b.Local) < b.Config.MessageQueue {
 | 
							if len(b.Local) < b.MessageQueue {
 | 
				
			||||||
			if len(b.Local) == b.Config.MessageQueue-1 {
 | 
								if len(b.Local) == b.MessageQueue-1 {
 | 
				
			||||||
				text = text + " <message clipped>"
 | 
									text = text + " <message clipped>"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
								b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			flog.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
 | 
								b.Log.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return "", nil
 | 
						return "", nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) doSend() {
 | 
					func (b *Birc) doSend() {
 | 
				
			||||||
	rate := time.Millisecond * time.Duration(b.Config.MessageDelay)
 | 
						rate := time.Millisecond * time.Duration(b.MessageDelay)
 | 
				
			||||||
	throttle := time.NewTicker(rate)
 | 
						throttle := time.NewTicker(rate)
 | 
				
			||||||
	for msg := range b.Local {
 | 
						for msg := range b.Local {
 | 
				
			||||||
		<-throttle.C
 | 
							<-throttle.C
 | 
				
			||||||
		if msg.Event == config.EVENT_USER_ACTION {
 | 
							if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
			b.i.Cmd.Action(msg.Channel, msg.Username+msg.Text)
 | 
								b.i.Cmd.Action(msg.Channel, msg.Username+msg.Text)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								b.Log.Debugf("Sending to channel %s", msg.Channel)
 | 
				
			||||||
			b.i.Cmd.Message(msg.Channel, msg.Username+msg.Text)
 | 
								b.i.Cmd.Message(msg.Channel, msg.Username+msg.Text)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -242,7 +265,7 @@ func (b *Birc) endNames(client *girc.Client, event girc.Event) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) {
 | 
					func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) {
 | 
				
			||||||
	flog.Debug("Registering callbacks")
 | 
						b.Log.Debug("Registering callbacks")
 | 
				
			||||||
	i := b.i
 | 
						i := b.i
 | 
				
			||||||
	b.Nick = event.Params[0]
 | 
						b.Nick = event.Params[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -261,107 +284,132 @@ func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
 | 
					func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
 | 
				
			||||||
	if len(event.Params) == 0 {
 | 
						if len(event.Params) == 0 {
 | 
				
			||||||
		flog.Debugf("handleJoinPart: empty Params? %#v", event)
 | 
							b.Log.Debugf("handleJoinPart: empty Params? %#v", event)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	channel := event.Params[0]
 | 
						channel := strings.ToLower(event.Params[0])
 | 
				
			||||||
	if event.Command == "KICK" {
 | 
						if event.Command == "KICK" {
 | 
				
			||||||
		flog.Infof("Got kicked from %s by %s", channel, event.Source.Name)
 | 
							b.Log.Infof("Got kicked from %s by %s", channel, event.Source.Name)
 | 
				
			||||||
		time.Sleep(time.Duration(b.Config.RejoinDelay) * time.Second)
 | 
							time.Sleep(time.Duration(b.GetInt("RejoinDelay")) * time.Second)
 | 
				
			||||||
		b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
							b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if event.Command == "QUIT" {
 | 
						if event.Command == "QUIT" {
 | 
				
			||||||
		if event.Source.Name == b.Nick && strings.Contains(event.Trailing, "Ping timeout") {
 | 
							if event.Source.Name == b.Nick && strings.Contains(event.Trailing, "Ping timeout") {
 | 
				
			||||||
			flog.Infof("%s reconnecting ..", b.Account)
 | 
								b.Log.Infof("%s reconnecting ..", b.Account)
 | 
				
			||||||
			b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: channel, Account: b.Account, Event: config.EVENT_FAILURE}
 | 
								b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: channel, Account: b.Account, Event: config.EVENT_FAILURE}
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if event.Source.Name != b.Nick {
 | 
						if event.Source.Name != b.Nick {
 | 
				
			||||||
		flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
							if b.GetBool("nosendjoinpart") {
 | 
				
			||||||
		b.Remote <- config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
				
			||||||
 | 
							msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
				
			||||||
 | 
							b.Log.Debugf("<= Message is %#v", msg)
 | 
				
			||||||
 | 
							b.Remote <- msg
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("handle %#v", event)
 | 
						b.Log.Debugf("handle %#v", event)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handleNotice(client *girc.Client, event girc.Event) {
 | 
					func (b *Birc) handleNotice(client *girc.Client, event girc.Event) {
 | 
				
			||||||
	if strings.Contains(event.String(), "This nickname is registered") && event.Source.Name == b.Config.NickServNick {
 | 
						if strings.Contains(event.String(), "This nickname is registered") && event.Source.Name == b.GetString("NickServNick") {
 | 
				
			||||||
		b.i.Cmd.Message(b.Config.NickServNick, "IDENTIFY "+b.Config.NickServPassword)
 | 
							b.i.Cmd.Message(b.GetString("NickServNick"), "IDENTIFY "+b.GetString("NickServPassword"))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		b.handlePrivMsg(client, event)
 | 
							b.handlePrivMsg(client, event)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handleOther(client *girc.Client, event girc.Event) {
 | 
					func (b *Birc) handleOther(client *girc.Client, event girc.Event) {
 | 
				
			||||||
 | 
						if b.GetInt("DebugLevel") == 1 {
 | 
				
			||||||
 | 
							if event.Command != "CLIENT_STATE_UPDATED" &&
 | 
				
			||||||
 | 
								event.Command != "CLIENT_GENERAL_UPDATED" {
 | 
				
			||||||
 | 
								b.Log.Debugf("%#v", event.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	switch event.Command {
 | 
						switch event.Command {
 | 
				
			||||||
	case "372", "375", "376", "250", "251", "252", "253", "254", "255", "265", "266", "002", "003", "004", "005":
 | 
						case "372", "375", "376", "250", "251", "252", "253", "254", "255", "265", "266", "002", "003", "004", "005":
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("%#v", event.String())
 | 
						b.Log.Debugf("%#v", event.String())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) {
 | 
					func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) {
 | 
				
			||||||
	if strings.EqualFold(b.Config.NickServNick, "Q@CServe.quakenet.org") {
 | 
						if strings.EqualFold(b.GetString("NickServNick"), "Q@CServe.quakenet.org") {
 | 
				
			||||||
		flog.Debugf("Authenticating %s against %s", b.Config.NickServUsername, b.Config.NickServNick)
 | 
							b.Log.Debugf("Authenticating %s against %s", b.GetString("NickServUsername"), b.GetString("NickServNick"))
 | 
				
			||||||
		b.i.Cmd.Message(b.Config.NickServNick, "AUTH "+b.Config.NickServUsername+" "+b.Config.NickServPassword)
 | 
							b.i.Cmd.Message(b.GetString("NickServNick"), "AUTH "+b.GetString("NickServUsername")+" "+b.GetString("NickServPassword"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
 | 
					func (b *Birc) skipPrivMsg(event girc.Event) bool {
 | 
				
			||||||
 | 
						// Our nick can be changed
 | 
				
			||||||
	b.Nick = b.i.GetNick()
 | 
						b.Nick = b.i.GetNick()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// freenode doesn't send 001 as first reply
 | 
						// freenode doesn't send 001 as first reply
 | 
				
			||||||
	if event.Command == "NOTICE" {
 | 
						if event.Command == "NOTICE" {
 | 
				
			||||||
		return
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// don't forward queries to the bot
 | 
						// don't forward queries to the bot
 | 
				
			||||||
	if event.Params[0] == b.Nick {
 | 
						if event.Params[0] == b.Nick {
 | 
				
			||||||
		return
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// don't forward message from ourself
 | 
						// don't forward message from ourself
 | 
				
			||||||
	if event.Source.Name == b.Nick {
 | 
						if event.Source.Name == b.Nick {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
 | 
				
			||||||
 | 
						if b.skipPrivMsg(event) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
						rmsg := config.Message{Username: event.Source.Name, Channel: strings.ToLower(event.Params[0]), Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
				
			||||||
	flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event)
 | 
						b.Log.Debugf("== Receiving PRIVMSG: %s %s %#v", event.Source.Name, event.Trailing, event)
 | 
				
			||||||
	msg := ""
 | 
					
 | 
				
			||||||
 | 
						// set action event
 | 
				
			||||||
	if event.IsAction() {
 | 
						if event.IsAction() {
 | 
				
			||||||
		rmsg.Event = config.EVENT_USER_ACTION
 | 
							rmsg.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	msg += event.StripAction()
 | 
					
 | 
				
			||||||
 | 
						// strip action, we made an event if it was an action
 | 
				
			||||||
 | 
						rmsg.Text += event.StripAction()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// strip IRC colors
 | 
						// strip IRC colors
 | 
				
			||||||
	re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
 | 
						re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
 | 
				
			||||||
	msg = re.ReplaceAllString(msg, "")
 | 
						rmsg.Text = re.ReplaceAllString(rmsg.Text, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// start detecting the charset
 | 
				
			||||||
	var r io.Reader
 | 
						var r io.Reader
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	mycharset := b.Config.Charset
 | 
						mycharset := b.GetString("Charset")
 | 
				
			||||||
	if mycharset == "" {
 | 
						if mycharset == "" {
 | 
				
			||||||
		// detect what were sending so that we convert it to utf-8
 | 
							// detect what were sending so that we convert it to utf-8
 | 
				
			||||||
		detector := chardet.NewTextDetector()
 | 
							detector := chardet.NewTextDetector()
 | 
				
			||||||
		result, err := detector.DetectBest([]byte(msg))
 | 
							result, err := detector.DetectBest([]byte(rmsg.Text))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			flog.Infof("detection failed for msg: %#v", msg)
 | 
								b.Log.Infof("detection failed for rmsg.Text: %#v", rmsg.Text)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		flog.Debugf("detected %s confidence %#v", result.Charset, result.Confidence)
 | 
							b.Log.Debugf("detected %s confidence %#v", result.Charset, result.Confidence)
 | 
				
			||||||
		mycharset = result.Charset
 | 
							mycharset = result.Charset
 | 
				
			||||||
		// if we're not sure, just pick ISO-8859-1
 | 
							// if we're not sure, just pick ISO-8859-1
 | 
				
			||||||
		if result.Confidence < 80 {
 | 
							if result.Confidence < 80 {
 | 
				
			||||||
			mycharset = "ISO-8859-1"
 | 
								mycharset = "ISO-8859-1"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r, err = charset.NewReader(mycharset, strings.NewReader(msg))
 | 
						r, err = charset.NewReader(mycharset, strings.NewReader(rmsg.Text))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Errorf("charset to utf-8 conversion failed: %s", err)
 | 
							b.Log.Errorf("charset to utf-8 conversion failed: %s", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	output, _ := ioutil.ReadAll(r)
 | 
						output, _ := ioutil.ReadAll(r)
 | 
				
			||||||
	msg = string(output)
 | 
						rmsg.Text = string(output)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flog.Debugf("Sending message from %s on %s to gateway", event.Params[0], b.Account)
 | 
						b.Log.Debugf("<= Sending message from %s on %s to gateway", event.Params[0], b.Account)
 | 
				
			||||||
	rmsg.Text = msg
 | 
					 | 
				
			||||||
	b.Remote <- rmsg
 | 
						b.Remote <- rmsg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -369,13 +417,13 @@ func (b *Birc) handleTopicWhoTime(client *girc.Client, event girc.Event) {
 | 
				
			|||||||
	parts := strings.Split(event.Params[2], "!")
 | 
						parts := strings.Split(event.Params[2], "!")
 | 
				
			||||||
	t, err := strconv.ParseInt(event.Params[3], 10, 64)
 | 
						t, err := strconv.ParseInt(event.Params[3], 10, 64)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Errorf("Invalid time stamp: %s", event.Params[3])
 | 
							b.Log.Errorf("Invalid time stamp: %s", event.Params[3])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	user := parts[0]
 | 
						user := parts[0]
 | 
				
			||||||
	if len(parts) > 1 {
 | 
						if len(parts) > 1 {
 | 
				
			||||||
		user += " [" + parts[1] + "]"
 | 
							user += " [" + parts[1] + "]"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("%s: Topic set by %s [%s]", event.Command, user, time.Unix(t, 0))
 | 
						b.Log.Debugf("%s: Topic set by %s [%s]", event.Command, user, time.Unix(t, 0))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) nicksPerRow() int {
 | 
					func (b *Birc) nicksPerRow() int {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,15 +2,15 @@ package bmatrix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
 | 
						matrix "github.com/matterbridge/gomatrix"
 | 
				
			||||||
	"mime"
 | 
						"mime"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
					 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
					 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	matrix "github.com/matrix-org/gomatrix"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Bmatrix struct {
 | 
					type Bmatrix struct {
 | 
				
			||||||
@@ -18,42 +18,33 @@ type Bmatrix struct {
 | 
				
			|||||||
	UserID  string
 | 
						UserID  string
 | 
				
			||||||
	RoomMap map[string]string
 | 
						RoomMap map[string]string
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "matrix"
 | 
						b := &Bmatrix{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bmatrix {
 | 
					 | 
				
			||||||
	b := &Bmatrix{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
	b.RoomMap = make(map[string]string)
 | 
						b.RoomMap = make(map[string]string)
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmatrix) Connect() error {
 | 
					func (b *Bmatrix) Connect() error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	flog.Infof("Connecting %s", b.Config.Server)
 | 
						b.Log.Infof("Connecting %s", b.GetString("Server"))
 | 
				
			||||||
	b.mc, err = matrix.NewClient(b.Config.Server, "", "")
 | 
						b.mc, err = matrix.NewClient(b.GetString("Server"), "", "")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	resp, err := b.mc.Login(&matrix.ReqLogin{
 | 
						resp, err := b.mc.Login(&matrix.ReqLogin{
 | 
				
			||||||
		Type:     "m.login.password",
 | 
							Type:     "m.login.password",
 | 
				
			||||||
		User:     b.Config.Login,
 | 
							User:     b.GetString("Login"),
 | 
				
			||||||
		Password: b.Config.Password,
 | 
							Password: b.GetString("Password"),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.mc.SetCredentials(resp.UserID, resp.AccessToken)
 | 
						b.mc.SetCredentials(resp.UserID, resp.AccessToken)
 | 
				
			||||||
	b.UserID = resp.UserID
 | 
						b.UserID = resp.UserID
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	go b.handlematrix()
 | 
						go b.handlematrix()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -74,58 +65,53 @@ func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
					func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
	// ignore delete messages
 | 
					
 | 
				
			||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
					 | 
				
			||||||
		return "", nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	channel := b.getRoomID(msg.Channel)
 | 
						channel := b.getRoomID(msg.Channel)
 | 
				
			||||||
	flog.Debugf("Sending to channel %s", channel)
 | 
						b.Log.Debugf("Channel %s maps to channel id %s", msg.Channel, channel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a action /me of the message
 | 
				
			||||||
	if msg.Event == config.EVENT_USER_ACTION {
 | 
						if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
		b.mc.SendMessageEvent(channel, "m.room.message",
 | 
							resp, err := b.mc.SendMessageEvent(channel, "m.room.message",
 | 
				
			||||||
			matrix.TextMessage{"m.emote", msg.Username + msg.Text})
 | 
								matrix.TextMessage{"m.emote", msg.Username + msg.Text})
 | 
				
			||||||
		return "", nil
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return resp.EventID, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete message
 | 
				
			||||||
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
 | 
							if msg.ID == "" {
 | 
				
			||||||
 | 
								return "", nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							resp, err := b.mc.RedactEvent(channel, msg.ID, &matrix.ReqRedact{})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return resp.EventID, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Upload a file if it exists
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.mc.SendText(channel, rmsg.Username+rmsg.Text)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
							// check if we have files to upload (from slack, telegram or mattermost)
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
								return b.handleUploadFile(&msg, channel)
 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
					 | 
				
			||||||
				content := bytes.NewReader(*fi.Data)
 | 
					 | 
				
			||||||
				sp := strings.Split(fi.Name, ".")
 | 
					 | 
				
			||||||
				mtype := mime.TypeByExtension("." + sp[len(sp)-1])
 | 
					 | 
				
			||||||
				if strings.Contains(mtype, "image") ||
 | 
					 | 
				
			||||||
					strings.Contains(mtype, "video") {
 | 
					 | 
				
			||||||
					flog.Debugf("uploading file: %s %s", fi.Name, mtype)
 | 
					 | 
				
			||||||
					res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						flog.Errorf("file upload failed: %#v", err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if strings.Contains(mtype, "video") {
 | 
					 | 
				
			||||||
						flog.Debugf("sendVideo %s", res.ContentURI)
 | 
					 | 
				
			||||||
						_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI)
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							flog.Errorf("sendVideo failed: %#v", err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if strings.Contains(mtype, "image") {
 | 
					 | 
				
			||||||
						flog.Debugf("sendImage %s", res.ContentURI)
 | 
					 | 
				
			||||||
						_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							flog.Errorf("sendImage failed: %#v", err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					flog.Debugf("result: %#v", res)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return "", nil
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.mc.SendText(channel, msg.Username+msg.Text)
 | 
						// Edit message if we have an ID
 | 
				
			||||||
	return "", nil
 | 
						// matrix has no editing support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Post normal message
 | 
				
			||||||
 | 
						resp, err := b.mc.SendText(channel, msg.Username+msg.Text)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp.EventID, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmatrix) getRoomID(channel string) string {
 | 
					func (b *Bmatrix) getRoomID(channel string) string {
 | 
				
			||||||
@@ -138,64 +124,188 @@ func (b *Bmatrix) getRoomID(channel string) string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmatrix) handlematrix() error {
 | 
					func (b *Bmatrix) handlematrix() error {
 | 
				
			||||||
	syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
 | 
						syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
 | 
				
			||||||
	syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
 | 
						syncer.OnEventType("m.room.redaction", b.handleEvent)
 | 
				
			||||||
		flog.Debugf("Received: %#v", ev)
 | 
						syncer.OnEventType("m.room.message", b.handleEvent)
 | 
				
			||||||
		if (ev.Content["msgtype"].(string) == "m.text" ||
 | 
					 | 
				
			||||||
			ev.Content["msgtype"].(string) == "m.notice" ||
 | 
					 | 
				
			||||||
			ev.Content["msgtype"].(string) == "m.emote" ||
 | 
					 | 
				
			||||||
			ev.Content["msgtype"].(string) == "m.file" ||
 | 
					 | 
				
			||||||
			ev.Content["msgtype"].(string) == "m.image" ||
 | 
					 | 
				
			||||||
			ev.Content["msgtype"].(string) == "m.video") && ev.Sender != b.UserID {
 | 
					 | 
				
			||||||
			b.RLock()
 | 
					 | 
				
			||||||
			channel, ok := b.RoomMap[ev.RoomID]
 | 
					 | 
				
			||||||
			b.RUnlock()
 | 
					 | 
				
			||||||
			if !ok {
 | 
					 | 
				
			||||||
				flog.Debugf("Unknown room %s", ev.RoomID)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			username := ev.Sender[1:]
 | 
					 | 
				
			||||||
			if b.Config.NoHomeServerSuffix {
 | 
					 | 
				
			||||||
				re := regexp.MustCompile("(.*?):.*")
 | 
					 | 
				
			||||||
				username = re.ReplaceAllString(username, `$1`)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			rmsg := config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account, UserID: ev.Sender}
 | 
					 | 
				
			||||||
			if ev.Content["msgtype"].(string) == "m.emote" {
 | 
					 | 
				
			||||||
				rmsg.Event = config.EVENT_USER_ACTION
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if ev.Content["msgtype"].(string) == "m.image" ||
 | 
					 | 
				
			||||||
				ev.Content["msgtype"].(string) == "m.video" ||
 | 
					 | 
				
			||||||
				ev.Content["msgtype"].(string) == "m.file" {
 | 
					 | 
				
			||||||
				flog.Debugf("ev: %#v", ev)
 | 
					 | 
				
			||||||
				rmsg.Extra = make(map[string][]interface{})
 | 
					 | 
				
			||||||
				url := ev.Content["url"].(string)
 | 
					 | 
				
			||||||
				url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1)
 | 
					 | 
				
			||||||
				info := ev.Content["info"].(map[string]interface{})
 | 
					 | 
				
			||||||
				size := info["size"].(float64)
 | 
					 | 
				
			||||||
				name := ev.Content["body"].(string)
 | 
					 | 
				
			||||||
				flog.Debugf("trying to download %#v with size %#v", name, size)
 | 
					 | 
				
			||||||
				if size <= float64(b.General.MediaDownloadSize) {
 | 
					 | 
				
			||||||
					data, err := helper.DownloadFile(url)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						flog.Errorf("download %s failed %#v", url, err)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url))
 | 
					 | 
				
			||||||
						rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{Name: name, Data: data})
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				rmsg.Text = ""
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
 | 
					 | 
				
			||||||
			b.Remote <- rmsg
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		for {
 | 
							for {
 | 
				
			||||||
			if err := b.mc.Sync(); err != nil {
 | 
								if err := b.mc.Sync(); err != nil {
 | 
				
			||||||
				flog.Println("Sync() returned ", err)
 | 
									b.Log.Println("Sync() returned ", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmatrix) handleEvent(ev *matrix.Event) {
 | 
				
			||||||
 | 
						b.Log.Debugf("== Receiving event: %#v", ev)
 | 
				
			||||||
 | 
						if ev.Sender != b.UserID {
 | 
				
			||||||
 | 
							b.RLock()
 | 
				
			||||||
 | 
							channel, ok := b.RoomMap[ev.RoomID]
 | 
				
			||||||
 | 
							b.RUnlock()
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								b.Log.Debugf("Unknown room %s", ev.RoomID)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO download avatar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Create our message
 | 
				
			||||||
 | 
							rmsg := config.Message{Username: ev.Sender[1:], Channel: channel, Account: b.Account, UserID: ev.Sender, ID: ev.ID}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Text must be a string
 | 
				
			||||||
 | 
							if rmsg.Text, ok = ev.Content["body"].(string); !ok {
 | 
				
			||||||
 | 
								b.Log.Errorf("Content[body] wasn't a %T ?", rmsg.Text)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Remove homeserver suffix if configured
 | 
				
			||||||
 | 
							if b.GetBool("NoHomeServerSuffix") {
 | 
				
			||||||
 | 
								re := regexp.MustCompile("(.*?):.*")
 | 
				
			||||||
 | 
								rmsg.Username = re.ReplaceAllString(rmsg.Username, `$1`)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Delete event
 | 
				
			||||||
 | 
							if ev.Type == "m.room.redaction" {
 | 
				
			||||||
 | 
								rmsg.Event = config.EVENT_MSG_DELETE
 | 
				
			||||||
 | 
								rmsg.ID = ev.Redacts
 | 
				
			||||||
 | 
								rmsg.Text = config.EVENT_MSG_DELETE
 | 
				
			||||||
 | 
								b.Remote <- rmsg
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Do we have a /me action
 | 
				
			||||||
 | 
							if ev.Content["msgtype"].(string) == "m.emote" {
 | 
				
			||||||
 | 
								rmsg.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Do we have attachments
 | 
				
			||||||
 | 
							if b.containsAttachment(ev.Content) {
 | 
				
			||||||
 | 
								err := b.handleDownloadFile(&rmsg, ev.Content)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									b.Log.Errorf("download failed: %#v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
 | 
				
			||||||
 | 
							b.Remote <- rmsg
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleDownloadFile handles file download
 | 
				
			||||||
 | 
					func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, content map[string]interface{}) error {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							ok                        bool
 | 
				
			||||||
 | 
							url, name, msgtype, mtype string
 | 
				
			||||||
 | 
							info                      map[string]interface{}
 | 
				
			||||||
 | 
							size                      float64
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rmsg.Extra = make(map[string][]interface{})
 | 
				
			||||||
 | 
						if url, ok = content["url"].(string); !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("url isn't a %T", url)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						url = strings.Replace(url, "mxc://", b.GetString("Server")+"/_matrix/media/v1/download/", -1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if info, ok = content["info"].(map[string]interface{}); !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("info isn't a %T", info)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if size, ok = info["size"].(float64); !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("size isn't a %T", size)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if name, ok = content["body"].(string); !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("name isn't a %T", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if msgtype, ok = content["msgtype"].(string); !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("msgtype isn't a %T", msgtype)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if mtype, ok = info["mimetype"].(string); !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("mtype isn't a %T", mtype)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if we have an image uploaded without extension
 | 
				
			||||||
 | 
						if !strings.Contains(name, ".") {
 | 
				
			||||||
 | 
							if msgtype == "m.image" {
 | 
				
			||||||
 | 
								mext, _ := mime.ExtensionsByType(mtype)
 | 
				
			||||||
 | 
								if len(mext) > 0 {
 | 
				
			||||||
 | 
									name = name + mext[0]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// just a default .png extension if we don't have mime info
 | 
				
			||||||
 | 
								name = name + ".png"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if the size is ok
 | 
				
			||||||
 | 
						err := helper.HandleDownloadSize(b.Log, rmsg, name, int64(size), b.General)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// actually download the file
 | 
				
			||||||
 | 
						data, err := helper.DownloadFile(url)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("download %s failed %#v", url, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// add the downloaded data to the message
 | 
				
			||||||
 | 
						helper.HandleDownloadData(b.Log, rmsg, name, "", url, data, b.General)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleUploadFile handles native upload of files
 | 
				
			||||||
 | 
					func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string) (string, error) {
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							content := bytes.NewReader(*fi.Data)
 | 
				
			||||||
 | 
							sp := strings.Split(fi.Name, ".")
 | 
				
			||||||
 | 
							mtype := mime.TypeByExtension("." + sp[len(sp)-1])
 | 
				
			||||||
 | 
							if strings.Contains(mtype, "image") ||
 | 
				
			||||||
 | 
								strings.Contains(mtype, "video") {
 | 
				
			||||||
 | 
								if fi.Comment != "" {
 | 
				
			||||||
 | 
									_, err := b.mc.SendText(channel, msg.Username+fi.Comment)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										b.Log.Errorf("file comment failed: %#v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								b.Log.Debugf("uploading file: %s %s", fi.Name, mtype)
 | 
				
			||||||
 | 
								res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									b.Log.Errorf("file upload failed: %#v", err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if strings.Contains(mtype, "video") {
 | 
				
			||||||
 | 
									b.Log.Debugf("sendVideo %s", res.ContentURI)
 | 
				
			||||||
 | 
									_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										b.Log.Errorf("sendVideo failed: %#v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if strings.Contains(mtype, "image") {
 | 
				
			||||||
 | 
									b.Log.Debugf("sendImage %s", res.ContentURI)
 | 
				
			||||||
 | 
									_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										b.Log.Errorf("sendImage failed: %#v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								b.Log.Debugf("result: %#v", res)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// skipMessages returns true if this message should not be handled
 | 
				
			||||||
 | 
					func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool {
 | 
				
			||||||
 | 
						// Skip empty messages
 | 
				
			||||||
 | 
						if content["msgtype"] == nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Only allow image,video or file msgtypes
 | 
				
			||||||
 | 
						if !(content["msgtype"].(string) == "m.image" ||
 | 
				
			||||||
 | 
							content["msgtype"].(string) == "m.video" ||
 | 
				
			||||||
 | 
							content["msgtype"].(string) == "m.file") {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,52 +3,24 @@ package bmattermost
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterclient"
 | 
						"github.com/42wim/matterbridge/matterclient"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterhook"
 | 
						"github.com/42wim/matterbridge/matterhook"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MMhook struct {
 | 
					 | 
				
			||||||
	mh *matterhook.Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MMapi struct {
 | 
					 | 
				
			||||||
	mc    *matterclient.MMClient
 | 
					 | 
				
			||||||
	mmMap map[string]string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MMMessage struct {
 | 
					 | 
				
			||||||
	Text     string
 | 
					 | 
				
			||||||
	Channel  string
 | 
					 | 
				
			||||||
	Username string
 | 
					 | 
				
			||||||
	UserID   string
 | 
					 | 
				
			||||||
	ID       string
 | 
					 | 
				
			||||||
	Event    string
 | 
					 | 
				
			||||||
	Extra    map[string][]interface{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Bmattermost struct {
 | 
					type Bmattermost struct {
 | 
				
			||||||
	MMhook
 | 
						mh     *matterhook.Client
 | 
				
			||||||
	MMapi
 | 
						mc     *matterclient.MMClient
 | 
				
			||||||
	Config  *config.Protocol
 | 
						TeamID string
 | 
				
			||||||
	Remote  chan config.Message
 | 
						*bridge.Config
 | 
				
			||||||
	TeamId  string
 | 
						avatarMap map[string]string
 | 
				
			||||||
	Account string
 | 
					 | 
				
			||||||
	*config.BridgeConfig
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "mattermost"
 | 
						b := &Bmattermost{Config: cfg, avatarMap: make(map[string]string)}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bmattermost {
 | 
					 | 
				
			||||||
	b := &Bmattermost{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
	b.mmMap = make(map[string]string)
 | 
					 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,47 +29,47 @@ func (b *Bmattermost) Command(cmd string) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) Connect() error {
 | 
					func (b *Bmattermost) Connect() error {
 | 
				
			||||||
	if b.Config.WebhookBindAddress != "" {
 | 
						if b.GetString("WebhookBindAddress") != "" {
 | 
				
			||||||
		if b.Config.WebhookURL != "" {
 | 
							if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
			flog.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)")
 | 
								b.Log.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)")
 | 
				
			||||||
			b.mh = matterhook.New(b.Config.WebhookURL,
 | 
								b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
				matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
									matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
					BindAddress: b.Config.WebhookBindAddress})
 | 
										BindAddress: b.GetString("WebhookBindAddress")})
 | 
				
			||||||
		} else if b.Config.Token != "" {
 | 
							} else if b.GetString("Token") != "" {
 | 
				
			||||||
			flog.Info("Connecting using token (sending)")
 | 
								b.Log.Info("Connecting using token (sending)")
 | 
				
			||||||
			err := b.apiLogin()
 | 
								err := b.apiLogin()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if b.Config.Login != "" {
 | 
							} else if b.GetString("Login") != "" {
 | 
				
			||||||
			flog.Info("Connecting using login/password (sending)")
 | 
								b.Log.Info("Connecting using login/password (sending)")
 | 
				
			||||||
			err := b.apiLogin()
 | 
								err := b.apiLogin()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			flog.Info("Connecting using webhookbindaddress (receiving)")
 | 
								b.Log.Info("Connecting using webhookbindaddress (receiving)")
 | 
				
			||||||
			b.mh = matterhook.New(b.Config.WebhookURL,
 | 
								b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
				matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
									matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
					BindAddress: b.Config.WebhookBindAddress})
 | 
										BindAddress: b.GetString("WebhookBindAddress")})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		go b.handleMatter()
 | 
							go b.handleMatter()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		flog.Info("Connecting using webhookurl (sending)")
 | 
							b.Log.Info("Connecting using webhookurl (sending)")
 | 
				
			||||||
		b.mh = matterhook.New(b.Config.WebhookURL,
 | 
							b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
			matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
								matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
				DisableServer: true})
 | 
									DisableServer: true})
 | 
				
			||||||
		if b.Config.Token != "" {
 | 
							if b.GetString("Token") != "" {
 | 
				
			||||||
			flog.Info("Connecting using token (receiving)")
 | 
								b.Log.Info("Connecting using token (receiving)")
 | 
				
			||||||
			err := b.apiLogin()
 | 
								err := b.apiLogin()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			go b.handleMatter()
 | 
								go b.handleMatter()
 | 
				
			||||||
		} else if b.Config.Login != "" {
 | 
							} else if b.GetString("Login") != "" {
 | 
				
			||||||
			flog.Info("Connecting using login/password (receiving)")
 | 
								b.Log.Info("Connecting using login/password (receiving)")
 | 
				
			||||||
			err := b.apiLogin()
 | 
								err := b.apiLogin()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
@@ -105,23 +77,23 @@ func (b *Bmattermost) Connect() error {
 | 
				
			|||||||
			go b.handleMatter()
 | 
								go b.handleMatter()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	} else if b.Config.Token != "" {
 | 
						} else if b.GetString("Token") != "" {
 | 
				
			||||||
		flog.Info("Connecting using token (sending and receiving)")
 | 
							b.Log.Info("Connecting using token (sending and receiving)")
 | 
				
			||||||
		err := b.apiLogin()
 | 
							err := b.apiLogin()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		go b.handleMatter()
 | 
							go b.handleMatter()
 | 
				
			||||||
	} else if b.Config.Login != "" {
 | 
						} else if b.GetString("Login") != "" {
 | 
				
			||||||
		flog.Info("Connecting using login/password (sending and receiving)")
 | 
							b.Log.Info("Connecting using login/password (sending and receiving)")
 | 
				
			||||||
		err := b.apiLogin()
 | 
							err := b.apiLogin()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		go b.handleMatter()
 | 
							go b.handleMatter()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookBindAddress == "" && b.Config.WebhookURL == "" && b.Config.Login == "" && b.Config.Token == "" {
 | 
						if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" && b.GetString("Login") == "" && b.GetString("Token") == "" {
 | 
				
			||||||
		return errors.New("No connection method found. See that you have WebhookBindAddress, WebhookURL or Token/Login/Password/Server/Team configured.")
 | 
							return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Token/Login/Password/Server/Team configured")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -132,7 +104,7 @@ func (b *Bmattermost) Disconnect() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
	// we can only join channels using the API
 | 
						// we can only join channels using the API
 | 
				
			||||||
	if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" {
 | 
						if b.GetString("WebhookURL") == "" && b.GetString("WebhookBindAddress") == "" {
 | 
				
			||||||
		id := b.mc.GetChannelId(channel.Name, "")
 | 
							id := b.mc.GetChannelId(channel.Name, "")
 | 
				
			||||||
		if id == "" {
 | 
							if id == "" {
 | 
				
			||||||
			return fmt.Errorf("Could not find channel ID for channel %s", channel.Name)
 | 
								return fmt.Errorf("Could not find channel ID for channel %s", channel.Name)
 | 
				
			||||||
@@ -143,189 +115,330 @@ func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) Send(msg config.Message) (string, error) {
 | 
					func (b *Bmattermost) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a action /me of the message
 | 
				
			||||||
	if msg.Event == config.EVENT_USER_ACTION {
 | 
						if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
		msg.Text = "*" + msg.Text + "*"
 | 
							msg.Text = "*" + msg.Text + "*"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nick := msg.Username
 | 
					 | 
				
			||||||
	message := msg.Text
 | 
					 | 
				
			||||||
	channel := msg.Channel
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if b.Config.PrefixMessagesWithNick {
 | 
						// map the file SHA to our user (caches the avatar)
 | 
				
			||||||
		message = nick + message
 | 
						if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
				
			||||||
 | 
							return b.cacheAvatar(&msg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
					
 | 
				
			||||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
 | 
						// Use webhook to send the message
 | 
				
			||||||
		matterMessage.IconURL = msg.Avatar
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		matterMessage.Channel = channel
 | 
							return b.sendWebhook(msg)
 | 
				
			||||||
		matterMessage.UserName = nick
 | 
					 | 
				
			||||||
		matterMessage.Type = ""
 | 
					 | 
				
			||||||
		matterMessage.Text = message
 | 
					 | 
				
			||||||
		matterMessage.Text = message
 | 
					 | 
				
			||||||
		matterMessage.Props = make(map[string]interface{})
 | 
					 | 
				
			||||||
		matterMessage.Props["matterbridge"] = true
 | 
					 | 
				
			||||||
		err := b.mh.Send(matterMessage)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			flog.Info(err)
 | 
					 | 
				
			||||||
			return "", err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return "", nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete message
 | 
				
			||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		if msg.ID == "" {
 | 
							if msg.ID == "" {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return msg.ID, b.mc.DeleteMessage(msg.ID)
 | 
							return msg.ID, b.mc.DeleteMessage(msg.ID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Upload a file if it exists
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.mc.PostMessage(b.mc.GetChannelId(rmsg.Channel, ""), rmsg.Username+rmsg.Text)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
			var err error
 | 
								return b.handleUploadFile(&msg)
 | 
				
			||||||
			var res, id string
 | 
					 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
					 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
					 | 
				
			||||||
				id, err = b.mc.UploadFile(*fi.Data, b.mc.GetChannelId(channel, ""), fi.Name)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					flog.Debugf("ERROR %#v", err)
 | 
					 | 
				
			||||||
					return "", err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				message = fi.Comment
 | 
					 | 
				
			||||||
				if b.Config.PrefixMessagesWithNick {
 | 
					 | 
				
			||||||
					message = nick + fi.Comment
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				res, err = b.mc.PostMessageWithFiles(b.mc.GetChannelId(channel, ""), message, []string{id})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return res, err
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if msg.ID != "" {
 | 
					
 | 
				
			||||||
		return b.mc.EditMessage(msg.ID, message)
 | 
						// Prepend nick if configured
 | 
				
			||||||
 | 
						if b.GetBool("PrefixMessagesWithNick") {
 | 
				
			||||||
 | 
							msg.Text = msg.Username + msg.Text
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return b.mc.PostMessage(b.mc.GetChannelId(channel, ""), message)
 | 
					
 | 
				
			||||||
 | 
						// Edit message if we have an ID
 | 
				
			||||||
 | 
						if msg.ID != "" {
 | 
				
			||||||
 | 
							return b.mc.EditMessage(msg.ID, msg.Text)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Post normal message
 | 
				
			||||||
 | 
						return b.mc.PostMessage(b.mc.GetChannelId(msg.Channel, ""), msg.Text)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) handleMatter() {
 | 
					func (b *Bmattermost) handleMatter() {
 | 
				
			||||||
	mchan := make(chan *MMMessage)
 | 
						messages := make(chan *config.Message)
 | 
				
			||||||
	if b.Config.WebhookBindAddress != "" {
 | 
						if b.GetString("WebhookBindAddress") != "" {
 | 
				
			||||||
		flog.Debugf("Choosing webhooks based receiving")
 | 
							b.Log.Debugf("Choosing webhooks based receiving")
 | 
				
			||||||
		go b.handleMatterHook(mchan)
 | 
							go b.handleMatterHook(messages)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if b.Config.Token != "" {
 | 
							if b.GetString("Token") != "" {
 | 
				
			||||||
			flog.Debugf("Choosing token based receiving")
 | 
								b.Log.Debugf("Choosing token based receiving")
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			flog.Debugf("Choosing login/password based receiving")
 | 
								b.Log.Debugf("Choosing login/password based receiving")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		go b.handleMatterClient(mchan)
 | 
							go b.handleMatterClient(messages)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for message := range mchan {
 | 
						var ok bool
 | 
				
			||||||
		rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID, ID: message.ID, Event: message.Event, Extra: message.Extra}
 | 
						for message := range messages {
 | 
				
			||||||
		text, ok := b.replaceAction(message.Text)
 | 
							message.Avatar = helper.GetAvatar(b.avatarMap, message.UserID, b.General)
 | 
				
			||||||
 | 
							message.Account = b.Account
 | 
				
			||||||
 | 
							message.Text, ok = b.replaceAction(message.Text)
 | 
				
			||||||
		if ok {
 | 
							if ok {
 | 
				
			||||||
			rmsg.Event = config.EVENT_USER_ACTION
 | 
								message.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		rmsg.Text = text
 | 
							b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
 | 
				
			||||||
		flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
 | 
							b.Log.Debugf("<= Message is %#v", message)
 | 
				
			||||||
		flog.Debugf("Message is %#v", rmsg)
 | 
							b.Remote <- *message
 | 
				
			||||||
		b.Remote <- rmsg
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
 | 
					func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
 | 
				
			||||||
	for message := range b.mc.MessageChan {
 | 
						for message := range b.mc.MessageChan {
 | 
				
			||||||
		flog.Debugf("%#v", message.Raw.Data)
 | 
							b.Log.Debugf("%#v", message.Raw.Data)
 | 
				
			||||||
		if message.Type == "system_join_leave" ||
 | 
					
 | 
				
			||||||
			message.Type == "system_join_channel" ||
 | 
							if b.skipMessage(message) {
 | 
				
			||||||
			message.Type == "system_leave_channel" {
 | 
								b.Log.Debugf("Skipped message: %#v", message)
 | 
				
			||||||
			flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
					 | 
				
			||||||
			b.Remote <- config.Message{Username: "system", Text: message.Text, Channel: message.Channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (message.Raw.Event == "post_edited") && b.Config.EditDisable {
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m := &MMMessage{Extra: make(map[string][]interface{})}
 | 
							// only download avatars if we have a place to upload them (configured mediaserver)
 | 
				
			||||||
 | 
							if b.General.MediaServerUpload != "" {
 | 
				
			||||||
 | 
								b.handleDownloadAvatar(message.UserID, message.Channel)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							b.Log.Debugf("== Receiving event %#v", message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rmsg := &config.Message{Username: message.Username, UserID: message.UserID, Channel: message.Channel, Text: message.Text, ID: message.Post.Id, Extra: make(map[string][]interface{})}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// handle mattermost post properties (override username and attachments)
 | 
				
			||||||
		props := message.Post.Props
 | 
							props := message.Post.Props
 | 
				
			||||||
		if props != nil {
 | 
							if props != nil {
 | 
				
			||||||
			if _, ok := props["matterbridge"].(bool); ok {
 | 
					 | 
				
			||||||
				flog.Debugf("sent by matterbridge, ignoring")
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if _, ok := props["override_username"].(string); ok {
 | 
								if _, ok := props["override_username"].(string); ok {
 | 
				
			||||||
				message.Username = props["override_username"].(string)
 | 
									rmsg.Username = props["override_username"].(string)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if _, ok := props["attachments"].([]interface{}); ok {
 | 
								if _, ok := props["attachments"].([]interface{}); ok {
 | 
				
			||||||
				m.Extra["attachments"] = props["attachments"].([]interface{})
 | 
									rmsg.Extra["attachments"] = props["attachments"].([]interface{})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// do not post our own messages back to irc
 | 
					
 | 
				
			||||||
		// only listen to message from our team
 | 
							// create a text for bridges that don't support native editing
 | 
				
			||||||
		if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited" || message.Raw.Event == "post_deleted") &&
 | 
							if message.Raw.Event == "post_edited" && !b.GetBool("EditDisable") {
 | 
				
			||||||
			b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
 | 
								rmsg.Text = message.Text + b.GetString("EditSuffix")
 | 
				
			||||||
			// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
 | 
							}
 | 
				
			||||||
			if message.Post.HasReactions {
 | 
					
 | 
				
			||||||
				continue
 | 
							if message.Raw.Event == "post_deleted" {
 | 
				
			||||||
			}
 | 
								rmsg.Event = config.EVENT_MSG_DELETE
 | 
				
			||||||
			flog.Debugf("Receiving from matterclient %#v", message)
 | 
							}
 | 
				
			||||||
			m.UserID = message.UserID
 | 
					
 | 
				
			||||||
			m.Username = message.Username
 | 
							if len(message.Post.FileIds) > 0 {
 | 
				
			||||||
			m.Channel = message.Channel
 | 
								for _, id := range message.Post.FileIds {
 | 
				
			||||||
			m.Text = message.Text
 | 
									err := b.handleDownloadFile(rmsg, id)
 | 
				
			||||||
			m.ID = message.Post.Id
 | 
									if err != nil {
 | 
				
			||||||
			if message.Raw.Event == "post_edited" && !b.Config.EditDisable {
 | 
										b.Log.Errorf("download failed: %s", err)
 | 
				
			||||||
				m.Text = message.Text + b.Config.EditSuffix
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if message.Raw.Event == "post_deleted" {
 | 
					 | 
				
			||||||
				m.Event = config.EVENT_MSG_DELETE
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if len(message.Post.FileIds) > 0 {
 | 
					 | 
				
			||||||
				for _, link := range b.mc.GetFileLinks(message.Post.FileIds) {
 | 
					 | 
				
			||||||
					m.Text = m.Text + "\n" + link
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			mchan <- m
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							messages <- rmsg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) handleMatterHook(mchan chan *MMMessage) {
 | 
					func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		message := b.mh.Receive()
 | 
							message := b.mh.Receive()
 | 
				
			||||||
		flog.Debugf("Receiving from matterhook %#v", message)
 | 
							b.Log.Debugf("Receiving from matterhook %#v", message)
 | 
				
			||||||
		m := &MMMessage{}
 | 
							messages <- &config.Message{UserID: message.UserID, Username: message.UserName, Text: message.Text, Channel: message.ChannelName}
 | 
				
			||||||
		m.UserID = message.UserID
 | 
					 | 
				
			||||||
		m.Username = message.UserName
 | 
					 | 
				
			||||||
		m.Text = message.Text
 | 
					 | 
				
			||||||
		m.Channel = message.ChannelName
 | 
					 | 
				
			||||||
		mchan <- m
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) apiLogin() error {
 | 
					func (b *Bmattermost) apiLogin() error {
 | 
				
			||||||
	password := b.Config.Password
 | 
						password := b.GetString("Password")
 | 
				
			||||||
	if b.Config.Token != "" {
 | 
						if b.GetString("Token") != "" {
 | 
				
			||||||
		password = "MMAUTHTOKEN=" + b.Config.Token
 | 
							password = "MMAUTHTOKEN=" + b.GetString("Token")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.mc = matterclient.New(b.Config.Login, password,
 | 
						b.mc = matterclient.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"))
 | 
				
			||||||
		b.Config.Team, b.Config.Server)
 | 
						if b.GetBool("debug") {
 | 
				
			||||||
	b.mc.SkipTLSVerify = b.Config.SkipTLSVerify
 | 
							b.mc.SetLogLevel("debug")
 | 
				
			||||||
	b.mc.NoTLS = b.Config.NoTLS
 | 
						}
 | 
				
			||||||
	flog.Infof("Connecting %s (team: %s) on %s", b.Config.Login, b.Config.Team, b.Config.Server)
 | 
						b.mc.SkipTLSVerify = b.GetBool("SkipTLSVerify")
 | 
				
			||||||
 | 
						b.mc.NoTLS = b.GetBool("NoTLS")
 | 
				
			||||||
 | 
						b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
 | 
				
			||||||
	err := b.mc.Login()
 | 
						err := b.mc.Login()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	b.TeamId = b.mc.GetTeamId()
 | 
						b.TeamID = b.mc.GetTeamId()
 | 
				
			||||||
	go b.mc.WsReceiver()
 | 
						go b.mc.WsReceiver()
 | 
				
			||||||
	go b.mc.StatusLoop()
 | 
						go b.mc.StatusLoop()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// replaceAction replace the message with the correct action (/me) code
 | 
				
			||||||
func (b *Bmattermost) replaceAction(text string) (string, bool) {
 | 
					func (b *Bmattermost) replaceAction(text string) (string, bool) {
 | 
				
			||||||
	if strings.HasPrefix(text, "*") && strings.HasSuffix(text, "*") {
 | 
						if strings.HasPrefix(text, "*") && strings.HasSuffix(text, "*") {
 | 
				
			||||||
		return strings.Replace(text, "*", "", -1), true
 | 
							return strings.Replace(text, "*", "", -1), true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return text, false
 | 
						return text, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmattermost) cacheAvatar(msg *config.Message) (string, error) {
 | 
				
			||||||
 | 
						fi := msg.Extra["file"][0].(config.FileInfo)
 | 
				
			||||||
 | 
						/* if we have a sha we have successfully uploaded the file to the media server,
 | 
				
			||||||
 | 
						so we can now cache the sha */
 | 
				
			||||||
 | 
						if fi.SHA != "" {
 | 
				
			||||||
 | 
							b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
 | 
				
			||||||
 | 
							b.avatarMap[msg.UserID] = fi.SHA
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleDownloadAvatar downloads the avatar of userid from channel
 | 
				
			||||||
 | 
					// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
 | 
				
			||||||
 | 
					// logs an error message if it fails
 | 
				
			||||||
 | 
					func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
 | 
				
			||||||
 | 
						rmsg := config.Message{Username: "system", Text: "avatar", Channel: channel, Account: b.Account, UserID: userid, Event: config.EVENT_AVATAR_DOWNLOAD, Extra: make(map[string][]interface{})}
 | 
				
			||||||
 | 
						if _, ok := b.avatarMap[userid]; !ok {
 | 
				
			||||||
 | 
							data, resp := b.mc.Client.GetProfileImage(userid, "")
 | 
				
			||||||
 | 
							if resp.Error != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Error(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							helper.HandleDownloadData(b.Log, &rmsg, userid+".png", rmsg.Text, "", &data, b.General)
 | 
				
			||||||
 | 
							b.Remote <- rmsg
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleDownloadFile handles file download
 | 
				
			||||||
 | 
					func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
 | 
				
			||||||
 | 
						url, _ := b.mc.Client.GetFileLink(id)
 | 
				
			||||||
 | 
						finfo, resp := b.mc.Client.GetFileInfo(id)
 | 
				
			||||||
 | 
						if resp.Error != nil {
 | 
				
			||||||
 | 
							return resp.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err := helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						data, resp := b.mc.Client.DownloadFile(id, true)
 | 
				
			||||||
 | 
						if resp.Error != nil {
 | 
				
			||||||
 | 
							return resp.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleUploadFile handles native upload of files
 | 
				
			||||||
 | 
					func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						var res, id string
 | 
				
			||||||
 | 
						channelID := b.mc.GetChannelId(msg.Channel, "")
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							id, err = b.mc.UploadFile(*fi.Data, channelID, fi.Name)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							msg.Text = fi.Comment
 | 
				
			||||||
 | 
							if b.GetBool("PrefixMessagesWithNick") {
 | 
				
			||||||
 | 
								msg.Text = msg.Username + msg.Text
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							res, err = b.mc.PostMessageWithFiles(channelID, msg.Text, []string{id})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// sendWebhook uses the configured WebhookURL to send the message
 | 
				
			||||||
 | 
					func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {
 | 
				
			||||||
 | 
						// skip events
 | 
				
			||||||
 | 
						if msg.Event != "" {
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if b.GetBool("PrefixMessagesWithNick") {
 | 
				
			||||||
 | 
							msg.Text = msg.Username + msg.Text
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							// this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: rmsg.Channel, UserName: rmsg.Username, Text: rmsg.Text, Props: make(map[string]interface{})}
 | 
				
			||||||
 | 
								matterMessage.Props["matterbridge"] = true
 | 
				
			||||||
 | 
								b.mh.Send(matterMessage)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// webhook doesn't support file uploads, so we add the url manually
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
									fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
									if fi.URL != "" {
 | 
				
			||||||
 | 
										msg.Text += fi.URL
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: msg.Channel, UserName: msg.Username, Text: msg.Text, Props: make(map[string]interface{})}
 | 
				
			||||||
 | 
						if msg.Avatar != "" {
 | 
				
			||||||
 | 
							matterMessage.IconURL = msg.Avatar
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						matterMessage.Props["matterbridge"] = true
 | 
				
			||||||
 | 
						err := b.mh.Send(matterMessage)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Info(err)
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// skipMessages returns true if this message should not be handled
 | 
				
			||||||
 | 
					func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
 | 
				
			||||||
 | 
						// Handle join/leave
 | 
				
			||||||
 | 
						if message.Type == "system_join_leave" ||
 | 
				
			||||||
 | 
							message.Type == "system_join_channel" ||
 | 
				
			||||||
 | 
							message.Type == "system_leave_channel" {
 | 
				
			||||||
 | 
							if b.GetBool("nosendjoinpart") {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							b.Log.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
				
			||||||
 | 
							b.Remote <- config.Message{Username: "system", Text: message.Text, Channel: message.Channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle edited messages
 | 
				
			||||||
 | 
						if (message.Raw.Event == "post_edited") && b.GetBool("EditDisable") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore messages sent from matterbridge
 | 
				
			||||||
 | 
						if message.Post.Props != nil {
 | 
				
			||||||
 | 
							if _, ok := message.Post.Props["matterbridge"].(bool); ok {
 | 
				
			||||||
 | 
								b.Log.Debugf("sent by matterbridge, ignoring")
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore messages sent from a user logged in as the bot
 | 
				
			||||||
 | 
						if b.mc.User.Username == message.Username {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
 | 
				
			||||||
 | 
						if message.Post.HasReactions {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ignore messages from other teams than ours
 | 
				
			||||||
 | 
						if message.Raw.Data["team_id"].(string) != b.TeamID {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// only handle posted, edited or deleted events
 | 
				
			||||||
 | 
						if !(message.Raw.Event == "posted" || message.Raw.Event == "post_edited" || message.Raw.Event == "post_deleted") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
package brocketchat
 | 
					package brocketchat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/42wim/matterbridge/hook/rockethook"
 | 
						"github.com/42wim/matterbridge/hook/rockethook"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterhook"
 | 
						"github.com/42wim/matterbridge/matterhook"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MMhook struct {
 | 
					type MMhook struct {
 | 
				
			||||||
@@ -14,18 +15,11 @@ type MMhook struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Brocketchat struct {
 | 
					type Brocketchat struct {
 | 
				
			||||||
	MMhook
 | 
						MMhook
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "rocketchat"
 | 
						return &Brocketchat{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Brocketchat {
 | 
					 | 
				
			||||||
	return &Brocketchat{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Brocketchat) Command(cmd string) string {
 | 
					func (b *Brocketchat) Command(cmd string) string {
 | 
				
			||||||
@@ -33,11 +27,11 @@ func (b *Brocketchat) Command(cmd string) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Brocketchat) Connect() error {
 | 
					func (b *Brocketchat) Connect() error {
 | 
				
			||||||
	flog.Info("Connecting webhooks")
 | 
						b.Log.Info("Connecting webhooks")
 | 
				
			||||||
	b.mh = matterhook.New(b.Config.WebhookURL,
 | 
						b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
		matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
							matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
			DisableServer: true})
 | 
								DisableServer: true})
 | 
				
			||||||
	b.rh = rockethook.New(b.Config.WebhookURL, rockethook.Config{BindAddress: b.Config.WebhookBindAddress})
 | 
						b.rh = rockethook.New(b.GetString("WebhookURL"), rockethook.Config{BindAddress: b.GetString("WebhookBindAddress")})
 | 
				
			||||||
	go b.handleRocketHook()
 | 
						go b.handleRocketHook()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -56,15 +50,30 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
	matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: rmsg.Channel, UserName: rmsg.Username, Text: rmsg.Text}
 | 
				
			||||||
 | 
								b.mh.Send(matterMessage)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
									fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
									if fi.URL != "" {
 | 
				
			||||||
 | 
										msg.Text += fi.URL
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL")}
 | 
				
			||||||
	matterMessage.Channel = msg.Channel
 | 
						matterMessage.Channel = msg.Channel
 | 
				
			||||||
	matterMessage.UserName = msg.Username
 | 
						matterMessage.UserName = msg.Username
 | 
				
			||||||
	matterMessage.Type = ""
 | 
						matterMessage.Type = ""
 | 
				
			||||||
	matterMessage.Text = msg.Text
 | 
						matterMessage.Text = msg.Text
 | 
				
			||||||
	err := b.mh.Send(matterMessage)
 | 
						err := b.mh.Send(matterMessage)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Info(err)
 | 
							b.Log.Info(err)
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return "", nil
 | 
						return "", nil
 | 
				
			||||||
@@ -73,12 +82,12 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
func (b *Brocketchat) handleRocketHook() {
 | 
					func (b *Brocketchat) handleRocketHook() {
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		message := b.rh.Receive()
 | 
							message := b.rh.Receive()
 | 
				
			||||||
		flog.Debugf("Receiving from rockethook %#v", message)
 | 
							b.Log.Debugf("Receiving from rockethook %#v", message)
 | 
				
			||||||
		// do not loop
 | 
							// do not loop
 | 
				
			||||||
		if message.UserName == b.Config.Nick {
 | 
							if message.UserName == b.GetString("Nick") {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		flog.Debugf("Sending message from %s on %s to gateway", message.UserName, b.Account)
 | 
							b.Log.Debugf("<= Sending message from %s on %s to gateway", message.UserName, b.Account)
 | 
				
			||||||
		b.Remote <- config.Message{Text: message.Text, Username: message.UserName, Channel: message.ChannelName, Account: b.Account, UserID: message.UserID}
 | 
							b.Remote <- config.Message{Text: message.Text, Username: message.UserName, Channel: message.ChannelName, Account: b.Account, UserID: message.UserID}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,46 +4,34 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterhook"
 | 
						"github.com/42wim/matterbridge/matterhook"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						"github.com/nlopes/slack"
 | 
				
			||||||
	"github.com/matterbridge/slack"
 | 
					 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MMMessage struct {
 | 
					 | 
				
			||||||
	Text     string
 | 
					 | 
				
			||||||
	Channel  string
 | 
					 | 
				
			||||||
	Username string
 | 
					 | 
				
			||||||
	UserID   string
 | 
					 | 
				
			||||||
	Raw      *slack.MessageEvent
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Bslack struct {
 | 
					type Bslack struct {
 | 
				
			||||||
	mh       *matterhook.Client
 | 
						mh         *matterhook.Client
 | 
				
			||||||
	sc       *slack.Client
 | 
						sc         *slack.Client
 | 
				
			||||||
	rtm      *slack.RTM
 | 
						rtm        *slack.RTM
 | 
				
			||||||
	Plus     bool
 | 
						Users      []slack.User
 | 
				
			||||||
	Users    []slack.User
 | 
						Usergroups []slack.UserGroup
 | 
				
			||||||
	si       *slack.Info
 | 
						si         *slack.Info
 | 
				
			||||||
	channels []slack.Channel
 | 
						channels   []slack.Channel
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
 | 
						sync.RWMutex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					const messageDeleted = "message_deleted"
 | 
				
			||||||
var protocol = "slack"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
						return &Bslack{Config: cfg}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bslack {
 | 
					 | 
				
			||||||
	return &Bslack{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) Command(cmd string) string {
 | 
					func (b *Bslack) Command(cmd string) string {
 | 
				
			||||||
@@ -51,71 +39,76 @@ func (b *Bslack) Command(cmd string) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) Connect() error {
 | 
					func (b *Bslack) Connect() error {
 | 
				
			||||||
	if b.Config.WebhookBindAddress != "" {
 | 
						b.RLock()
 | 
				
			||||||
		if b.Config.WebhookURL != "" {
 | 
						defer b.RUnlock()
 | 
				
			||||||
			flog.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)")
 | 
						if b.GetString("WebhookBindAddress") != "" {
 | 
				
			||||||
			b.mh = matterhook.New(b.Config.WebhookURL,
 | 
							if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
				matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
								b.Log.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)")
 | 
				
			||||||
					BindAddress: b.Config.WebhookBindAddress})
 | 
								b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
		} else if b.Config.Token != "" {
 | 
									matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
			flog.Info("Connecting using token (sending)")
 | 
										BindAddress: b.GetString("WebhookBindAddress")})
 | 
				
			||||||
			b.sc = slack.New(b.Config.Token)
 | 
							} else if b.GetString("Token") != "" {
 | 
				
			||||||
 | 
								b.Log.Info("Connecting using token (sending)")
 | 
				
			||||||
 | 
								b.sc = slack.New(b.GetString("Token"))
 | 
				
			||||||
			b.rtm = b.sc.NewRTM()
 | 
								b.rtm = b.sc.NewRTM()
 | 
				
			||||||
			go b.rtm.ManageConnection()
 | 
								go b.rtm.ManageConnection()
 | 
				
			||||||
			flog.Info("Connecting using webhookbindaddress (receiving)")
 | 
								b.Log.Info("Connecting using webhookbindaddress (receiving)")
 | 
				
			||||||
			b.mh = matterhook.New(b.Config.WebhookURL,
 | 
								b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
				matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
									matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
					BindAddress: b.Config.WebhookBindAddress})
 | 
										BindAddress: b.GetString("WebhookBindAddress")})
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			flog.Info("Connecting using webhookbindaddress (receiving)")
 | 
								b.Log.Info("Connecting using webhookbindaddress (receiving)")
 | 
				
			||||||
			b.mh = matterhook.New(b.Config.WebhookURL,
 | 
								b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
				matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
									matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
					BindAddress: b.Config.WebhookBindAddress})
 | 
										BindAddress: b.GetString("WebhookBindAddress")})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		go b.handleSlack()
 | 
							go b.handleSlack()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		flog.Info("Connecting using webhookurl (sending)")
 | 
							b.Log.Info("Connecting using webhookurl (sending)")
 | 
				
			||||||
		b.mh = matterhook.New(b.Config.WebhookURL,
 | 
							b.mh = matterhook.New(b.GetString("WebhookURL"),
 | 
				
			||||||
			matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
 | 
								matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
 | 
				
			||||||
				DisableServer: true})
 | 
									DisableServer: true})
 | 
				
			||||||
		if b.Config.Token != "" {
 | 
							if b.GetString("Token") != "" {
 | 
				
			||||||
			flog.Info("Connecting using token (receiving)")
 | 
								b.Log.Info("Connecting using token (receiving)")
 | 
				
			||||||
			b.sc = slack.New(b.Config.Token)
 | 
								b.sc = slack.New(b.GetString("Token"))
 | 
				
			||||||
			b.rtm = b.sc.NewRTM()
 | 
								b.rtm = b.sc.NewRTM()
 | 
				
			||||||
			go b.rtm.ManageConnection()
 | 
								go b.rtm.ManageConnection()
 | 
				
			||||||
			go b.handleSlack()
 | 
								go b.handleSlack()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if b.Config.Token != "" {
 | 
						} else if b.GetString("Token") != "" {
 | 
				
			||||||
		flog.Info("Connecting using token (sending and receiving)")
 | 
							b.Log.Info("Connecting using token (sending and receiving)")
 | 
				
			||||||
		b.sc = slack.New(b.Config.Token)
 | 
							b.sc = slack.New(b.GetString("Token"))
 | 
				
			||||||
		b.rtm = b.sc.NewRTM()
 | 
							b.rtm = b.sc.NewRTM()
 | 
				
			||||||
		go b.rtm.ManageConnection()
 | 
							go b.rtm.ManageConnection()
 | 
				
			||||||
		go b.handleSlack()
 | 
							go b.handleSlack()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookBindAddress == "" && b.Config.WebhookURL == "" && b.Config.Token == "" {
 | 
						if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" && b.GetString("Token") == "" {
 | 
				
			||||||
		return errors.New("No connection method found. See that you have WebhookBindAddress, WebhookURL or Token configured.")
 | 
							return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Token configured")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) Disconnect() error {
 | 
					func (b *Bslack) Disconnect() error {
 | 
				
			||||||
	return nil
 | 
						return b.rtm.Disconnect()
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
	// we can only join channels using the API
 | 
						// we can only join channels using the API
 | 
				
			||||||
	if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" {
 | 
						if b.sc != nil {
 | 
				
			||||||
		if strings.HasPrefix(b.Config.Token, "xoxb") {
 | 
							if strings.HasPrefix(b.GetString("Token"), "xoxb") {
 | 
				
			||||||
			// TODO check if bot has already joined channel
 | 
								// TODO check if bot has already joined channel
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_, err := b.sc.JoinChannel(channel.Name)
 | 
							_, err := b.sc.JoinChannel(channel.Name)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if err.Error() != "name_taken" {
 | 
								switch err.Error() {
 | 
				
			||||||
				return err
 | 
								case "name_taken", "restricted_action":
 | 
				
			||||||
 | 
								case "default":
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -123,48 +116,25 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
					func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a action /me of the message
 | 
				
			||||||
	if msg.Event == config.EVENT_USER_ACTION {
 | 
						if msg.Event == config.EVENT_USER_ACTION {
 | 
				
			||||||
		msg.Text = "_" + msg.Text + "_"
 | 
							msg.Text = "_" + msg.Text + "_"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nick := msg.Username
 | 
					
 | 
				
			||||||
	message := msg.Text
 | 
						// Use webhook to send the message
 | 
				
			||||||
	channel := msg.Channel
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
	if b.Config.PrefixMessagesWithNick {
 | 
							return b.sendWebhook(msg)
 | 
				
			||||||
		message = nick + " " + message
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.WebhookURL != "" {
 | 
					
 | 
				
			||||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
 | 
						// get the slack channel
 | 
				
			||||||
		matterMessage.Channel = channel
 | 
						schannel, err := b.getChannelByName(msg.Channel)
 | 
				
			||||||
		matterMessage.UserName = nick
 | 
					 | 
				
			||||||
		matterMessage.Type = ""
 | 
					 | 
				
			||||||
		matterMessage.Text = message
 | 
					 | 
				
			||||||
		err := b.mh.Send(matterMessage)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			flog.Info(err)
 | 
					 | 
				
			||||||
			return "", err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return "", nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	schannel, err := b.getChannelByName(channel)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	np := slack.NewPostMessageParameters()
 | 
					 | 
				
			||||||
	if b.Config.PrefixMessagesWithNick {
 | 
					 | 
				
			||||||
		np.AsUser = true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	np.Username = nick
 | 
					 | 
				
			||||||
	np.IconURL = config.GetIconURL(&msg, &b.Config)
 | 
					 | 
				
			||||||
	if msg.Avatar != "" {
 | 
					 | 
				
			||||||
		np.IconURL = msg.Avatar
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	np.Attachments = append(np.Attachments, slack.Attachment{CallbackID: "matterbridge"})
 | 
					 | 
				
			||||||
	np.Attachments = append(np.Attachments, b.createAttach(msg.Extra)...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// replace mentions
 | 
					 | 
				
			||||||
	np.LinkNames = 1
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete message
 | 
				
			||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		// some protocols echo deletes, but with empty ID
 | 
							// some protocols echo deletes, but with empty ID
 | 
				
			||||||
		if msg.ID == "" {
 | 
							if msg.ID == "" {
 | 
				
			||||||
@@ -172,42 +142,73 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		// we get a "slack <ID>", split it
 | 
							// we get a "slack <ID>", split it
 | 
				
			||||||
		ts := strings.Fields(msg.ID)
 | 
							ts := strings.Fields(msg.ID)
 | 
				
			||||||
		b.sc.DeleteMessage(schannel.ID, ts[1])
 | 
							_, _, err := b.sc.DeleteMessage(schannel.ID, ts[1])
 | 
				
			||||||
		return "", nil
 | 
							if err != nil {
 | 
				
			||||||
	}
 | 
								return msg.ID, err
 | 
				
			||||||
	// if we have no ID it means we're creating a new message, not updating an existing one
 | 
							}
 | 
				
			||||||
	if msg.ID != "" {
 | 
							return msg.ID, nil
 | 
				
			||||||
		ts := strings.Fields(msg.ID)
 | 
					 | 
				
			||||||
		b.sc.UpdateMessage(schannel.ID, ts[1], message)
 | 
					 | 
				
			||||||
		return "", nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						// Prepend nick if configured
 | 
				
			||||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
						if b.GetBool("PrefixMessagesWithNick") {
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
							msg.Text = msg.Username + msg.Text
 | 
				
			||||||
			var err error
 | 
						}
 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
					
 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
						// Edit message if we have an ID
 | 
				
			||||||
				_, err = b.sc.UploadFile(slack.FileUploadParameters{
 | 
						if msg.ID != "" {
 | 
				
			||||||
					Reader:         bytes.NewReader(*fi.Data),
 | 
							ts := strings.Fields(msg.ID)
 | 
				
			||||||
					Filename:       fi.Name,
 | 
							_, _, _, err := b.sc.UpdateMessage(schannel.ID, ts[1], msg.Text)
 | 
				
			||||||
					Channels:       []string{schannel.ID},
 | 
							if err != nil {
 | 
				
			||||||
					InitialComment: fi.Comment,
 | 
								return msg.ID, err
 | 
				
			||||||
				})
 | 
							}
 | 
				
			||||||
				if err != nil {
 | 
							return msg.ID, nil
 | 
				
			||||||
					flog.Errorf("uploadfile %#v", err)
 | 
						}
 | 
				
			||||||
				}
 | 
					
 | 
				
			||||||
			}
 | 
						// create slack new post parameters
 | 
				
			||||||
 | 
						np := slack.NewPostMessageParameters()
 | 
				
			||||||
 | 
						if b.GetBool("PrefixMessagesWithNick") {
 | 
				
			||||||
 | 
							np.AsUser = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						np.Username = msg.Username
 | 
				
			||||||
 | 
						np.LinkNames = 1 // replace mentions
 | 
				
			||||||
 | 
						np.IconURL = config.GetIconURL(&msg, b.GetString("iconurl"))
 | 
				
			||||||
 | 
						if msg.Avatar != "" {
 | 
				
			||||||
 | 
							np.IconURL = msg.Avatar
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// add a callback ID so we can see we created it
 | 
				
			||||||
 | 
						np.Attachments = append(np.Attachments, slack.Attachment{CallbackID: "matterbridge"})
 | 
				
			||||||
 | 
						// add file attachments
 | 
				
			||||||
 | 
						np.Attachments = append(np.Attachments, b.createAttach(msg.Extra)...)
 | 
				
			||||||
 | 
						// add slack attachments (from another slack bridge)
 | 
				
			||||||
 | 
						if len(msg.Extra["slack_attachment"]) > 0 {
 | 
				
			||||||
 | 
							for _, attach := range msg.Extra["slack_attachment"] {
 | 
				
			||||||
 | 
								np.Attachments = append(np.Attachments, attach.([]slack.Attachment)...)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, id, err := b.sc.PostMessage(schannel.ID, message, np)
 | 
						// Upload a file if it exists
 | 
				
			||||||
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.sc.PostMessage(schannel.ID, rmsg.Username+rmsg.Text, np)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// check if we have files to upload (from slack, telegram or mattermost)
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								b.handleUploadFile(&msg, schannel.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Post normal message
 | 
				
			||||||
 | 
						_, id, err := b.sc.PostMessage(schannel.ID, msg.Text, np)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return "slack " + id, nil
 | 
						return "slack " + id, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bslack) Reload(cfg *bridge.Config) (string, error) {
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) getAvatar(user string) string {
 | 
					func (b *Bslack) getAvatar(user string) string {
 | 
				
			||||||
	var avatar string
 | 
						var avatar string
 | 
				
			||||||
	if b.Users != nil {
 | 
						if b.Users != nil {
 | 
				
			||||||
@@ -245,145 +246,61 @@ func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) handleSlack() {
 | 
					func (b *Bslack) handleSlack() {
 | 
				
			||||||
	mchan := make(chan *MMMessage)
 | 
						messages := make(chan *config.Message)
 | 
				
			||||||
	if b.Config.WebhookBindAddress != "" {
 | 
						if b.GetString("WebhookBindAddress") != "" {
 | 
				
			||||||
		flog.Debugf("Choosing webhooks based receiving")
 | 
							b.Log.Debugf("Choosing webhooks based receiving")
 | 
				
			||||||
		go b.handleMatterHook(mchan)
 | 
							go b.handleMatterHook(messages)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		flog.Debugf("Choosing token based receiving")
 | 
							b.Log.Debugf("Choosing token based receiving")
 | 
				
			||||||
		go b.handleSlackClient(mchan)
 | 
							go b.handleSlackClient(messages)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	time.Sleep(time.Second)
 | 
						time.Sleep(time.Second)
 | 
				
			||||||
	flog.Debug("Start listening for Slack messages")
 | 
						b.Log.Debug("Start listening for Slack messages")
 | 
				
			||||||
	for message := range mchan {
 | 
						for message := range messages {
 | 
				
			||||||
		// do not send messages from ourself
 | 
							b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
 | 
				
			||||||
		if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && message.Username == b.si.User.Name {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (message.Text == "" || message.Username == "") && message.Raw.SubType != "message_deleted" {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		text := message.Text
 | 
					 | 
				
			||||||
		text = b.replaceURL(text)
 | 
					 | 
				
			||||||
		text = html.UnescapeString(text)
 | 
					 | 
				
			||||||
		flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
 | 
					 | 
				
			||||||
		msg := config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID, ID: "slack " + message.Raw.Timestamp, Extra: make(map[string][]interface{})}
 | 
					 | 
				
			||||||
		if message.Raw.SubType == "me_message" {
 | 
					 | 
				
			||||||
			msg.Event = config.EVENT_USER_ACTION
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Raw.SubType == "channel_leave" || message.Raw.SubType == "channel_join" {
 | 
					 | 
				
			||||||
			msg.Username = "system"
 | 
					 | 
				
			||||||
			msg.Event = config.EVENT_JOIN_LEAVE
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// edited messages have a submessage, use this timestamp
 | 
					 | 
				
			||||||
		if message.Raw.SubMessage != nil {
 | 
					 | 
				
			||||||
			msg.ID = "slack " + message.Raw.SubMessage.Timestamp
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Raw.SubType == "message_deleted" {
 | 
					 | 
				
			||||||
			msg.Text = config.EVENT_MSG_DELETE
 | 
					 | 
				
			||||||
			msg.Event = config.EVENT_MSG_DELETE
 | 
					 | 
				
			||||||
			msg.ID = "slack " + message.Raw.DeletedTimestamp
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
							// cleanup the message
 | 
				
			||||||
		if message.Raw.File != nil {
 | 
							message.Text = b.replaceMention(message.Text)
 | 
				
			||||||
			// limit to 1MB for now
 | 
							message.Text = b.replaceVariable(message.Text)
 | 
				
			||||||
			if message.Raw.File.Size <= b.General.MediaDownloadSize {
 | 
							message.Text = b.replaceChannel(message.Text)
 | 
				
			||||||
				comment := ""
 | 
							message.Text = b.replaceURL(message.Text)
 | 
				
			||||||
				data, err := b.downloadFile(message.Raw.File.URLPrivateDownload)
 | 
							message.Text = html.UnescapeString(message.Text)
 | 
				
			||||||
				if err != nil {
 | 
					
 | 
				
			||||||
					flog.Errorf("download %s failed %#v", message.Raw.File.URLPrivateDownload, err)
 | 
							// Add the avatar
 | 
				
			||||||
				} else {
 | 
							message.Avatar = b.getAvatar(message.Username)
 | 
				
			||||||
					results := regexp.MustCompile(`.*?commented: (.*)`).FindAllStringSubmatch(msg.Text, -1)
 | 
					
 | 
				
			||||||
					if len(results) > 0 {
 | 
							b.Log.Debugf("<= Message is %#v", message)
 | 
				
			||||||
						comment = results[0][1]
 | 
							b.Remote <- *message
 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: message.Raw.File.Name, Data: data, Comment: comment})
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		flog.Debugf("Message is %#v", msg)
 | 
					 | 
				
			||||||
		b.Remote <- msg
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
					func (b *Bslack) handleSlackClient(messages chan *config.Message) {
 | 
				
			||||||
	for msg := range b.rtm.IncomingEvents {
 | 
						for msg := range b.rtm.IncomingEvents {
 | 
				
			||||||
 | 
							if msg.Type != "user_typing" && msg.Type != "latency_report" {
 | 
				
			||||||
 | 
								b.Log.Debugf("== Receiving event %#v", msg.Data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		switch ev := msg.Data.(type) {
 | 
							switch ev := msg.Data.(type) {
 | 
				
			||||||
		case *slack.MessageEvent:
 | 
							case *slack.MessageEvent:
 | 
				
			||||||
			flog.Debugf("Receiving from slackclient %#v", ev)
 | 
								if b.skipMessageEvent(ev) {
 | 
				
			||||||
			if len(ev.Attachments) > 0 {
 | 
									b.Log.Debugf("Skipped message: %#v", ev)
 | 
				
			||||||
				// skip messages we made ourselves
 | 
					 | 
				
			||||||
				if ev.Attachments[0].CallbackID == "matterbridge" {
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if !b.Config.EditDisable && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
 | 
					 | 
				
			||||||
				flog.Debugf("SubMessage %#v", ev.SubMessage)
 | 
					 | 
				
			||||||
				ev.User = ev.SubMessage.User
 | 
					 | 
				
			||||||
				ev.Text = ev.SubMessage.Text + b.Config.EditSuffix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// it seems ev.SubMessage.Edited == nil when slack unfurls
 | 
					 | 
				
			||||||
				// do not forward these messages #266
 | 
					 | 
				
			||||||
				if ev.SubMessage.Edited == nil {
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// use our own func because rtm.GetChannelInfo doesn't work for private channels
 | 
					 | 
				
			||||||
			channel, err := b.getChannelByID(ev.Channel)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			m := &MMMessage{}
 | 
								rmsg, err := b.handleMessageEvent(ev)
 | 
				
			||||||
			if ev.BotID == "" && ev.SubType != "message_deleted" {
 | 
								if err != nil {
 | 
				
			||||||
				user, err := b.rtm.GetUserInfo(ev.User)
 | 
									b.Log.Errorf("%#v", err)
 | 
				
			||||||
				if err != nil {
 | 
									continue
 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				m.UserID = user.ID
 | 
					 | 
				
			||||||
				m.Username = user.Name
 | 
					 | 
				
			||||||
				if user.Profile.DisplayName != "" {
 | 
					 | 
				
			||||||
					m.Username = user.Profile.DisplayName
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			m.Channel = channel.Name
 | 
								messages <- rmsg
 | 
				
			||||||
			m.Text = ev.Text
 | 
					 | 
				
			||||||
			if m.Text == "" {
 | 
					 | 
				
			||||||
				for _, attach := range ev.Attachments {
 | 
					 | 
				
			||||||
					if attach.Text != "" {
 | 
					 | 
				
			||||||
						m.Text = attach.Text
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						m.Text = attach.Fallback
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			m.Raw = ev
 | 
					 | 
				
			||||||
			m.Text = b.replaceMention(m.Text)
 | 
					 | 
				
			||||||
			m.Text = b.replaceVariable(m.Text)
 | 
					 | 
				
			||||||
			m.Text = b.replaceChannel(m.Text)
 | 
					 | 
				
			||||||
			// when using webhookURL we can't check if it's our webhook or not for now
 | 
					 | 
				
			||||||
			if ev.BotID != "" && b.Config.WebhookURL == "" {
 | 
					 | 
				
			||||||
				bot, err := b.rtm.GetBotInfo(ev.BotID)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if bot.Name != "" {
 | 
					 | 
				
			||||||
					m.Username = bot.Name
 | 
					 | 
				
			||||||
					if ev.Username != "" {
 | 
					 | 
				
			||||||
						m.Username = ev.Username
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					m.UserID = bot.ID
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			mchan <- m
 | 
					 | 
				
			||||||
		case *slack.OutgoingErrorEvent:
 | 
							case *slack.OutgoingErrorEvent:
 | 
				
			||||||
			flog.Debugf("%#v", ev.Error())
 | 
								b.Log.Debugf("%#v", ev.Error())
 | 
				
			||||||
		case *slack.ChannelJoinedEvent:
 | 
							case *slack.ChannelJoinedEvent:
 | 
				
			||||||
			b.Users, _ = b.sc.GetUsers()
 | 
								b.Users, _ = b.sc.GetUsers()
 | 
				
			||||||
 | 
								b.Usergroups, _ = b.sc.GetUserGroups()
 | 
				
			||||||
		case *slack.ConnectedEvent:
 | 
							case *slack.ConnectedEvent:
 | 
				
			||||||
			b.channels = ev.Info.Channels
 | 
								b.channels = ev.Info.Channels
 | 
				
			||||||
			b.si = ev.Info
 | 
								b.si = ev.Info
 | 
				
			||||||
			b.Users, _ = b.sc.GetUsers()
 | 
								b.Users, _ = b.sc.GetUsers()
 | 
				
			||||||
 | 
								b.Usergroups, _ = b.sc.GetUserGroups()
 | 
				
			||||||
			// add private channels
 | 
								// add private channels
 | 
				
			||||||
			groups, _ := b.sc.GetGroups(true)
 | 
								groups, _ := b.sc.GetGroups(true)
 | 
				
			||||||
			for _, g := range groups {
 | 
								for _, g := range groups {
 | 
				
			||||||
@@ -393,27 +310,22 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
				
			|||||||
				b.channels = append(b.channels, *channel)
 | 
									b.channels = append(b.channels, *channel)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case *slack.InvalidAuthEvent:
 | 
							case *slack.InvalidAuthEvent:
 | 
				
			||||||
			flog.Fatalf("Invalid Token %#v", ev)
 | 
								b.Log.Fatalf("Invalid Token %#v", ev)
 | 
				
			||||||
 | 
							case *slack.ConnectionErrorEvent:
 | 
				
			||||||
 | 
								b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) handleMatterHook(mchan chan *MMMessage) {
 | 
					func (b *Bslack) handleMatterHook(messages chan *config.Message) {
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		message := b.mh.Receive()
 | 
							message := b.mh.Receive()
 | 
				
			||||||
		flog.Debugf("receiving from matterhook (slack) %#v", message)
 | 
							b.Log.Debugf("receiving from matterhook (slack) %#v", message)
 | 
				
			||||||
		m := &MMMessage{}
 | 
							if message.UserName == "slackbot" {
 | 
				
			||||||
		m.Username = message.UserName
 | 
					 | 
				
			||||||
		m.Text = message.Text
 | 
					 | 
				
			||||||
		m.Text = b.replaceMention(m.Text)
 | 
					 | 
				
			||||||
		m.Text = b.replaceVariable(m.Text)
 | 
					 | 
				
			||||||
		m.Text = b.replaceChannel(m.Text)
 | 
					 | 
				
			||||||
		m.Channel = message.ChannelName
 | 
					 | 
				
			||||||
		if m.Username == "slackbot" {
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		mchan <- m
 | 
							messages <- &config.Message{Username: message.UserName, Text: message.Text, Channel: message.ChannelName}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -429,6 +341,17 @@ func (b *Bslack) userName(id string) string {
 | 
				
			|||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					func (b *Bslack) userGroupName(id string) string {
 | 
				
			||||||
 | 
						for _, u := range b.Usergroups {
 | 
				
			||||||
 | 
							if u.ID == id {
 | 
				
			||||||
 | 
								return u.Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
 | 
					// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
 | 
				
			||||||
func (b *Bslack) replaceMention(text string) string {
 | 
					func (b *Bslack) replaceMention(text string) string {
 | 
				
			||||||
	results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
 | 
						results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
 | 
				
			||||||
@@ -449,9 +372,13 @@ func (b *Bslack) replaceChannel(text string) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// @see https://api.slack.com/docs/message-formatting#variables
 | 
					// @see https://api.slack.com/docs/message-formatting#variables
 | 
				
			||||||
func (b *Bslack) replaceVariable(text string) string {
 | 
					func (b *Bslack) replaceVariable(text string) string {
 | 
				
			||||||
	results := regexp.MustCompile(`<!([a-zA-Z0-9]+)(\|.+?)?>`).FindAllStringSubmatch(text, -1)
 | 
						results := regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`).FindAllStringSubmatch(text, -1)
 | 
				
			||||||
	for _, r := range results {
 | 
						for _, r := range results {
 | 
				
			||||||
		text = strings.Replace(text, r[0], "@"+r[1], -1)
 | 
							if r[2] != "" {
 | 
				
			||||||
 | 
								text = strings.Replace(text, r[0], "@"+r[2], -1)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								text = strings.Replace(text, r[0], "@"+r[1], -1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return text
 | 
						return text
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -488,23 +415,241 @@ func (b *Bslack) createAttach(extra map[string][]interface{}) []slack.Attachment
 | 
				
			|||||||
	return attachs
 | 
						return attachs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) downloadFile(url string) (*[]byte, error) {
 | 
					// handleDownloadFile handles file download
 | 
				
			||||||
	var buf bytes.Buffer
 | 
					func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File) error {
 | 
				
			||||||
	client := &http.Client{
 | 
						// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
				
			||||||
		Timeout: time.Second * 5,
 | 
						// limit to 1MB for now
 | 
				
			||||||
 | 
						comment := ""
 | 
				
			||||||
 | 
						results := regexp.MustCompile(`.*?commented: (.*)`).FindAllStringSubmatch(rmsg.Text, -1)
 | 
				
			||||||
 | 
						if len(results) > 0 {
 | 
				
			||||||
 | 
							comment = results[0][1]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req, err := http.NewRequest("GET", url, nil)
 | 
					
 | 
				
			||||||
 | 
						err := helper.HandleDownloadSize(b.Log, rmsg, file.Name, int64(file.Size), b.General)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req.Header.Add("Authorization", "Bearer "+b.Config.Token)
 | 
						// actually download the file
 | 
				
			||||||
	resp, err := client.Do(req)
 | 
						data, err := helper.DownloadFileAuth(file.URLPrivateDownload, "Bearer "+b.GetString("Token"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		resp.Body.Close()
 | 
							return fmt.Errorf("download %s failed %#v", file.URLPrivateDownload, err)
 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	io.Copy(&buf, resp.Body)
 | 
						// add the downloaded data to the message
 | 
				
			||||||
	data := buf.Bytes()
 | 
						helper.HandleDownloadData(b.Log, rmsg, file.Name, comment, file.URLPrivateDownload, data, b.General)
 | 
				
			||||||
	resp.Body.Close()
 | 
						return nil
 | 
				
			||||||
	return &data, nil
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleUploadFile handles native upload of files
 | 
				
			||||||
 | 
					func (b *Bslack) handleUploadFile(msg *config.Message, channelID string) (string, error) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							_, err = b.sc.UploadFile(slack.FileUploadParameters{
 | 
				
			||||||
 | 
								Reader:         bytes.NewReader(*fi.Data),
 | 
				
			||||||
 | 
								Filename:       fi.Name,
 | 
				
			||||||
 | 
								Channels:       []string{channelID},
 | 
				
			||||||
 | 
								InitialComment: fi.Comment,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("uploadfile %#v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleMessageEvent handles the message events
 | 
				
			||||||
 | 
					func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, error) {
 | 
				
			||||||
 | 
						// update the userlist on a channel_join
 | 
				
			||||||
 | 
						if ev.SubType == "channel_join" {
 | 
				
			||||||
 | 
							b.Users, _ = b.sc.GetUsers()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Edit message
 | 
				
			||||||
 | 
						if !b.GetBool("EditDisable") && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
 | 
				
			||||||
 | 
							b.Log.Debugf("SubMessage %#v", ev.SubMessage)
 | 
				
			||||||
 | 
							ev.User = ev.SubMessage.User
 | 
				
			||||||
 | 
							ev.Text = ev.SubMessage.Text + b.GetString("EditSuffix")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// use our own func because rtm.GetChannelInfo doesn't work for private channels
 | 
				
			||||||
 | 
						channel, err := b.getChannelByID(ev.Channel)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rmsg := config.Message{Text: ev.Text, Channel: channel.Name, Account: b.Account, ID: "slack " + ev.Timestamp, Extra: make(map[string][]interface{})}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// find the user id and name
 | 
				
			||||||
 | 
						if ev.BotID == "" && ev.SubType != messageDeleted && ev.SubType != "file_comment" {
 | 
				
			||||||
 | 
							user, err := b.rtm.GetUserInfo(ev.User)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rmsg.UserID = user.ID
 | 
				
			||||||
 | 
							rmsg.Username = user.Name
 | 
				
			||||||
 | 
							if user.Profile.DisplayName != "" {
 | 
				
			||||||
 | 
								rmsg.Username = user.Profile.DisplayName
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// See if we have some text in the attachments
 | 
				
			||||||
 | 
						if rmsg.Text == "" {
 | 
				
			||||||
 | 
							for _, attach := range ev.Attachments {
 | 
				
			||||||
 | 
								if attach.Text != "" {
 | 
				
			||||||
 | 
									rmsg.Text = attach.Text
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									rmsg.Text = attach.Fallback
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// when using webhookURL we can't check if it's our webhook or not for now
 | 
				
			||||||
 | 
						if ev.BotID != "" && b.GetString("WebhookURL") == "" {
 | 
				
			||||||
 | 
							bot, err := b.rtm.GetBotInfo(ev.BotID)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if bot.Name != "" {
 | 
				
			||||||
 | 
								rmsg.Username = bot.Name
 | 
				
			||||||
 | 
								if ev.Username != "" {
 | 
				
			||||||
 | 
									rmsg.Username = ev.Username
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								rmsg.UserID = bot.ID
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file comments are set by the system (because there is no username given)
 | 
				
			||||||
 | 
						if ev.SubType == "file_comment" {
 | 
				
			||||||
 | 
							rmsg.Username = "system"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// do we have a /me action
 | 
				
			||||||
 | 
						if ev.SubType == "me_message" {
 | 
				
			||||||
 | 
							rmsg.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle join/leave
 | 
				
			||||||
 | 
						if ev.SubType == "channel_leave" || ev.SubType == "channel_join" {
 | 
				
			||||||
 | 
							rmsg.Username = "system"
 | 
				
			||||||
 | 
							rmsg.Event = config.EVENT_JOIN_LEAVE
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// edited messages have a submessage, use this timestamp
 | 
				
			||||||
 | 
						if ev.SubMessage != nil {
 | 
				
			||||||
 | 
							rmsg.ID = "slack " + ev.SubMessage.Timestamp
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// deleted message event
 | 
				
			||||||
 | 
						if ev.SubType == messageDeleted {
 | 
				
			||||||
 | 
							rmsg.Text = config.EVENT_MSG_DELETE
 | 
				
			||||||
 | 
							rmsg.Event = config.EVENT_MSG_DELETE
 | 
				
			||||||
 | 
							rmsg.ID = "slack " + ev.DeletedTimestamp
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// topic change event
 | 
				
			||||||
 | 
						if ev.SubType == "channel_topic" || ev.SubType == "channel_purpose" {
 | 
				
			||||||
 | 
							rmsg.Event = config.EVENT_TOPIC_CHANGE
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Only deleted messages can have a empty username and text
 | 
				
			||||||
 | 
						if (rmsg.Text == "" || rmsg.Username == "") && ev.SubType != messageDeleted {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("empty message and not a deleted message")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// save the attachments, so that we can send them to other slack (compatible) bridges
 | 
				
			||||||
 | 
						if len(ev.Attachments) > 0 {
 | 
				
			||||||
 | 
							rmsg.Extra["slack_attachment"] = append(rmsg.Extra["slack_attachment"], ev.Attachments)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
				
			||||||
 | 
						if ev.File != nil {
 | 
				
			||||||
 | 
							err := b.handleDownloadFile(&rmsg, ev.File)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("download failed: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &rmsg, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// sendWebhook uses the configured WebhookURL to send the message
 | 
				
			||||||
 | 
					func (b *Bslack) sendWebhook(msg config.Message) (string, error) {
 | 
				
			||||||
 | 
						// skip events
 | 
				
			||||||
 | 
						if msg.Event != "" {
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if b.GetBool("PrefixMessagesWithNick") {
 | 
				
			||||||
 | 
							msg.Text = msg.Username + msg.Text
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							// this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: msg.Channel, UserName: rmsg.Username, Text: rmsg.Text}
 | 
				
			||||||
 | 
								b.mh.Send(matterMessage)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// webhook doesn't support file uploads, so we add the url manually
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
									fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
									if fi.URL != "" {
 | 
				
			||||||
 | 
										msg.Text += " " + fi.URL
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if we have native slack_attachments add them
 | 
				
			||||||
 | 
						var attachs []slack.Attachment
 | 
				
			||||||
 | 
						if len(msg.Extra["slack_attachment"]) > 0 {
 | 
				
			||||||
 | 
							for _, attach := range msg.Extra["slack_attachment"] {
 | 
				
			||||||
 | 
								attachs = append(attachs, attach.([]slack.Attachment)...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Attachments: attachs, Channel: msg.Channel, UserName: msg.Username, Text: msg.Text}
 | 
				
			||||||
 | 
						if msg.Avatar != "" {
 | 
				
			||||||
 | 
							matterMessage.IconURL = msg.Avatar
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err := b.mh.Send(matterMessage)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Error(err)
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// skipMessageEvent skips event that need to be skipped :-)
 | 
				
			||||||
 | 
					func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
 | 
				
			||||||
 | 
						if ev.SubType == "channel_leave" || ev.SubType == "channel_join" {
 | 
				
			||||||
 | 
							return b.GetBool("nosendjoinpart")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ignore pinned items
 | 
				
			||||||
 | 
						if ev.SubType == "pinned_item" || ev.SubType == "unpinned_item" {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// do not send messages from ourself
 | 
				
			||||||
 | 
						if b.GetString("WebhookURL") == "" && b.GetString("WebhookBindAddress") == "" && ev.Username == b.si.User.Name {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// skip messages we made ourselves
 | 
				
			||||||
 | 
						if len(ev.Attachments) > 0 {
 | 
				
			||||||
 | 
							if ev.Attachments[0].CallbackID == "matterbridge" {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !b.GetBool("EditDisable") && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
 | 
				
			||||||
 | 
							// it seems ev.SubMessage.Edited == nil when slack unfurls
 | 
				
			||||||
 | 
							// do not forward these messages #266
 | 
				
			||||||
 | 
							if ev.SubMessage.Edited == nil {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,11 @@ package bsshchat
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/shazow/ssh-chat/sshd"
 | 
						"github.com/shazow/ssh-chat/sshd"
 | 
				
			||||||
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -12,25 +14,18 @@ import (
 | 
				
			|||||||
type Bsshchat struct {
 | 
					type Bsshchat struct {
 | 
				
			||||||
	r *bufio.Scanner
 | 
						r *bufio.Scanner
 | 
				
			||||||
	w io.WriteCloser
 | 
						w io.WriteCloser
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "sshchat"
 | 
						return &Bsshchat{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bsshchat {
 | 
					 | 
				
			||||||
	return &Bsshchat{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bsshchat) Connect() error {
 | 
					func (b *Bsshchat) Connect() error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	flog.Infof("Connecting %s", b.Config.Server)
 | 
						b.Log.Infof("Connecting %s", b.GetString("Server"))
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		err = sshd.ConnectShell(b.Config.Server, b.Config.Nick, func(r io.Reader, w io.WriteCloser) error {
 | 
							err = sshd.ConnectShell(b.GetString("Server"), b.GetString("Nick"), func(r io.Reader, w io.WriteCloser) error {
 | 
				
			||||||
			b.r = bufio.NewScanner(r)
 | 
								b.r = bufio.NewScanner(r)
 | 
				
			||||||
			b.w = w
 | 
								b.w = w
 | 
				
			||||||
			b.r.Scan()
 | 
								b.r.Scan()
 | 
				
			||||||
@@ -40,10 +35,10 @@ func (b *Bsshchat) Connect() error {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
							b.Log.Debugf("%#v", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -60,11 +55,17 @@ func (b *Bsshchat) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.w.Write([]byte(rmsg.Username + rmsg.Text + "\r\n"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
								for _, f := range msg.Extra["file"] {
 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
									fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
									if fi.Comment != "" {
 | 
				
			||||||
 | 
										msg.Text += fi.Comment + ": "
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				if fi.URL != "" {
 | 
									if fi.URL != "" {
 | 
				
			||||||
					msg.Text = fi.URL
 | 
										msg.Text = fi.URL
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -86,10 +87,10 @@ func (b *Bsshchat) sshchatKeepAlive() chan bool {
 | 
				
			|||||||
		for {
 | 
							for {
 | 
				
			||||||
			select {
 | 
								select {
 | 
				
			||||||
			case <-ticker.C:
 | 
								case <-ticker.C:
 | 
				
			||||||
				flog.Debugf("PING")
 | 
									b.Log.Debugf("PING")
 | 
				
			||||||
				err := b.xc.PingC2S("", "")
 | 
									err := b.xc.PingC2S("", "")
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					flog.Debugf("PING failed %#v", err)
 | 
										b.Log.Debugf("PING failed %#v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case <-done:
 | 
								case <-done:
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@@ -123,7 +124,7 @@ func (b *Bsshchat) handleSshChat() error {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if !wait {
 | 
								if !wait {
 | 
				
			||||||
				flog.Debugf("message %#v", res)
 | 
									b.Log.Debugf("<= Message %#v", res)
 | 
				
			||||||
				rmsg := config.Message{Username: res[0], Text: strings.Join(res[1:], ":"), Channel: "sshchat", Account: b.Account, UserID: "nick"}
 | 
									rmsg := config.Message{Username: res[0], Text: strings.Join(res[1:], ":"), Channel: "sshchat", Account: b.Account, UserID: "nick"}
 | 
				
			||||||
				b.Remote <- rmsg
 | 
									b.Remote <- rmsg
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,11 @@ package bsteam
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/Philipp15b/go-steam"
 | 
						"github.com/Philipp15b/go-steam"
 | 
				
			||||||
	"github.com/Philipp15b/go-steam/protocol/steamlang"
 | 
						"github.com/Philipp15b/go-steam/protocol/steamlang"
 | 
				
			||||||
	"github.com/Philipp15b/go-steam/steamid"
 | 
						"github.com/Philipp15b/go-steam/steamid"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	//"io/ioutil"
 | 
						//"io/ioutil"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
@@ -18,31 +18,24 @@ type Bsteam struct {
 | 
				
			|||||||
	connected chan struct{}
 | 
						connected chan struct{}
 | 
				
			||||||
	userMap   map[steamid.SteamId]string
 | 
						userMap   map[steamid.SteamId]string
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "steam"
 | 
						b := &Bsteam{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bsteam {
 | 
					 | 
				
			||||||
	b := &Bsteam{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
	b.userMap = make(map[steamid.SteamId]string)
 | 
						b.userMap = make(map[steamid.SteamId]string)
 | 
				
			||||||
	b.connected = make(chan struct{})
 | 
						b.connected = make(chan struct{})
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bsteam) Connect() error {
 | 
					func (b *Bsteam) Connect() error {
 | 
				
			||||||
	flog.Info("Connecting")
 | 
						b.Log.Info("Connecting")
 | 
				
			||||||
	b.c = steam.NewClient()
 | 
						b.c = steam.NewClient()
 | 
				
			||||||
	go b.handleEvents()
 | 
						go b.handleEvents()
 | 
				
			||||||
	go b.c.Connect()
 | 
						go b.c.Connect()
 | 
				
			||||||
	select {
 | 
						select {
 | 
				
			||||||
	case <-b.connected:
 | 
						case <-b.connected:
 | 
				
			||||||
		flog.Info("Connection succeeded")
 | 
							b.Log.Info("Connection succeeded")
 | 
				
			||||||
	case <-time.After(time.Second * 30):
 | 
						case <-time.After(time.Second * 30):
 | 
				
			||||||
		return fmt.Errorf("connection timed out")
 | 
							return fmt.Errorf("connection timed out")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -88,18 +81,18 @@ func (b *Bsteam) getNick(id steamid.SteamId) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *Bsteam) handleEvents() {
 | 
					func (b *Bsteam) handleEvents() {
 | 
				
			||||||
	myLoginInfo := new(steam.LogOnDetails)
 | 
						myLoginInfo := new(steam.LogOnDetails)
 | 
				
			||||||
	myLoginInfo.Username = b.Config.Login
 | 
						myLoginInfo.Username = b.GetString("Login")
 | 
				
			||||||
	myLoginInfo.Password = b.Config.Password
 | 
						myLoginInfo.Password = b.GetString("Password")
 | 
				
			||||||
	myLoginInfo.AuthCode = b.Config.AuthCode
 | 
						myLoginInfo.AuthCode = b.GetString("AuthCode")
 | 
				
			||||||
	// Attempt to read existing auth hash to avoid steam guard.
 | 
						// Attempt to read existing auth hash to avoid steam guard.
 | 
				
			||||||
	// Maybe works
 | 
						// Maybe works
 | 
				
			||||||
	//myLoginInfo.SentryFileHash, _ = ioutil.ReadFile("sentry")
 | 
						//myLoginInfo.SentryFileHash, _ = ioutil.ReadFile("sentry")
 | 
				
			||||||
	for event := range b.c.Events() {
 | 
						for event := range b.c.Events() {
 | 
				
			||||||
		//flog.Info(event)
 | 
							//b.Log.Info(event)
 | 
				
			||||||
		switch e := event.(type) {
 | 
							switch e := event.(type) {
 | 
				
			||||||
		case *steam.ChatMsgEvent:
 | 
							case *steam.ChatMsgEvent:
 | 
				
			||||||
			flog.Debugf("Receiving ChatMsgEvent: %#v", e)
 | 
								b.Log.Debugf("Receiving ChatMsgEvent: %#v", e)
 | 
				
			||||||
			flog.Debugf("Sending message from %s on %s to gateway", b.getNick(e.ChatterId), b.Account)
 | 
								b.Log.Debugf("<= Sending message from %s on %s to gateway", b.getNick(e.ChatterId), b.Account)
 | 
				
			||||||
			var channel int64
 | 
								var channel int64
 | 
				
			||||||
			if e.ChatRoomId == 0 {
 | 
								if e.ChatRoomId == 0 {
 | 
				
			||||||
				channel = int64(e.ChatterId)
 | 
									channel = int64(e.ChatterId)
 | 
				
			||||||
@@ -110,7 +103,7 @@ func (b *Bsteam) handleEvents() {
 | 
				
			|||||||
			msg := config.Message{Username: b.getNick(e.ChatterId), Text: e.Message, Channel: strconv.FormatInt(channel, 10), Account: b.Account, UserID: strconv.FormatInt(int64(e.ChatterId), 10)}
 | 
								msg := config.Message{Username: b.getNick(e.ChatterId), Text: e.Message, Channel: strconv.FormatInt(channel, 10), Account: b.Account, UserID: strconv.FormatInt(int64(e.ChatterId), 10)}
 | 
				
			||||||
			b.Remote <- msg
 | 
								b.Remote <- msg
 | 
				
			||||||
		case *steam.PersonaStateEvent:
 | 
							case *steam.PersonaStateEvent:
 | 
				
			||||||
			flog.Debugf("PersonaStateEvent: %#v\n", e)
 | 
								b.Log.Debugf("PersonaStateEvent: %#v\n", e)
 | 
				
			||||||
			b.Lock()
 | 
								b.Lock()
 | 
				
			||||||
			b.userMap[e.FriendId] = e.Name
 | 
								b.userMap[e.FriendId] = e.Name
 | 
				
			||||||
			b.Unlock()
 | 
								b.Unlock()
 | 
				
			||||||
@@ -118,47 +111,47 @@ func (b *Bsteam) handleEvents() {
 | 
				
			|||||||
			b.c.Auth.LogOn(myLoginInfo)
 | 
								b.c.Auth.LogOn(myLoginInfo)
 | 
				
			||||||
		case *steam.MachineAuthUpdateEvent:
 | 
							case *steam.MachineAuthUpdateEvent:
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
				flog.Info("authupdate", e)
 | 
									b.Log.Info("authupdate", e)
 | 
				
			||||||
				flog.Info("hash", e.Hash)
 | 
									b.Log.Info("hash", e.Hash)
 | 
				
			||||||
				ioutil.WriteFile("sentry", e.Hash, 0666)
 | 
									ioutil.WriteFile("sentry", e.Hash, 0666)
 | 
				
			||||||
			*/
 | 
								*/
 | 
				
			||||||
		case *steam.LogOnFailedEvent:
 | 
							case *steam.LogOnFailedEvent:
 | 
				
			||||||
			flog.Info("Logon failed", e)
 | 
								b.Log.Info("Logon failed", e)
 | 
				
			||||||
			switch e.Result {
 | 
								switch e.Result {
 | 
				
			||||||
			case steamlang.EResult_AccountLogonDeniedNeedTwoFactorCode:
 | 
								case steamlang.EResult_AccountLogonDeniedNeedTwoFactorCode:
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					flog.Info("Steam guard isn't letting me in! Enter 2FA code:")
 | 
										b.Log.Info("Steam guard isn't letting me in! Enter 2FA code:")
 | 
				
			||||||
					var code string
 | 
										var code string
 | 
				
			||||||
					fmt.Scanf("%s", &code)
 | 
										fmt.Scanf("%s", &code)
 | 
				
			||||||
					myLoginInfo.TwoFactorCode = code
 | 
										myLoginInfo.TwoFactorCode = code
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case steamlang.EResult_AccountLogonDenied:
 | 
								case steamlang.EResult_AccountLogonDenied:
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					flog.Info("Steam guard isn't letting me in! Enter auth code:")
 | 
										b.Log.Info("Steam guard isn't letting me in! Enter auth code:")
 | 
				
			||||||
					var code string
 | 
										var code string
 | 
				
			||||||
					fmt.Scanf("%s", &code)
 | 
										fmt.Scanf("%s", &code)
 | 
				
			||||||
					myLoginInfo.AuthCode = code
 | 
										myLoginInfo.AuthCode = code
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				log.Errorf("LogOnFailedEvent: %#v ", e.Result)
 | 
									b.Log.Errorf("LogOnFailedEvent: %#v ", e.Result)
 | 
				
			||||||
				// TODO: Handle EResult_InvalidLoginAuthCode
 | 
									// TODO: Handle EResult_InvalidLoginAuthCode
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case *steam.LoggedOnEvent:
 | 
							case *steam.LoggedOnEvent:
 | 
				
			||||||
			flog.Debugf("LoggedOnEvent: %#v", e)
 | 
								b.Log.Debugf("LoggedOnEvent: %#v", e)
 | 
				
			||||||
			b.connected <- struct{}{}
 | 
								b.connected <- struct{}{}
 | 
				
			||||||
			flog.Debugf("setting online")
 | 
								b.Log.Debugf("setting online")
 | 
				
			||||||
			b.c.Social.SetPersonaState(steamlang.EPersonaState_Online)
 | 
								b.c.Social.SetPersonaState(steamlang.EPersonaState_Online)
 | 
				
			||||||
		case *steam.DisconnectedEvent:
 | 
							case *steam.DisconnectedEvent:
 | 
				
			||||||
			flog.Info("Disconnected")
 | 
								b.Log.Info("Disconnected")
 | 
				
			||||||
			flog.Info("Attempting to reconnect...")
 | 
								b.Log.Info("Attempting to reconnect...")
 | 
				
			||||||
			b.c.Connect()
 | 
								b.c.Connect()
 | 
				
			||||||
		case steam.FatalErrorEvent:
 | 
							case steam.FatalErrorEvent:
 | 
				
			||||||
			flog.Error(e)
 | 
								b.Log.Error(e)
 | 
				
			||||||
		case error:
 | 
							case error:
 | 
				
			||||||
			flog.Error(e)
 | 
								b.Log.Error(e)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			flog.Debugf("unknown event %#v", e)
 | 
								b.Log.Debugf("unknown event %#v", e)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,11 +6,11 @@ import (
 | 
				
			|||||||
	"html"
 | 
						"html"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type customHtml struct {
 | 
					type customHTML struct {
 | 
				
			||||||
	blackfriday.Renderer
 | 
						blackfriday.Renderer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) Paragraph(out *bytes.Buffer, text func() bool) {
 | 
					func (options *customHTML) Paragraph(out *bytes.Buffer, text func() bool) {
 | 
				
			||||||
	marker := out.Len()
 | 
						marker := out.Len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !text() {
 | 
						if !text() {
 | 
				
			||||||
@@ -20,32 +20,32 @@ func (options *customHtml) Paragraph(out *bytes.Buffer, text func() bool) {
 | 
				
			|||||||
	out.WriteString("\n")
 | 
						out.WriteString("\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) BlockCode(out *bytes.Buffer, text []byte, lang string) {
 | 
					func (options *customHTML) BlockCode(out *bytes.Buffer, text []byte, lang string) {
 | 
				
			||||||
	out.WriteString("<pre>")
 | 
						out.WriteString("<pre>")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	out.WriteString(html.EscapeString(string(text)))
 | 
						out.WriteString(html.EscapeString(string(text)))
 | 
				
			||||||
	out.WriteString("</pre>\n")
 | 
						out.WriteString("</pre>\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) Header(out *bytes.Buffer, text func() bool, level int, id string) {
 | 
					func (options *customHTML) Header(out *bytes.Buffer, text func() bool, level int, id string) {
 | 
				
			||||||
	options.Paragraph(out, text)
 | 
						options.Paragraph(out, text)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) HRule(out *bytes.Buffer) {
 | 
					func (options *customHTML) HRule(out *bytes.Buffer) {
 | 
				
			||||||
	out.WriteByte('\n')
 | 
						out.WriteByte('\n')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) BlockQuote(out *bytes.Buffer, text []byte) {
 | 
					func (options *customHTML) BlockQuote(out *bytes.Buffer, text []byte) {
 | 
				
			||||||
	out.WriteString("> ")
 | 
						out.WriteString("> ")
 | 
				
			||||||
	out.Write(text)
 | 
						out.Write(text)
 | 
				
			||||||
	out.WriteByte('\n')
 | 
						out.WriteByte('\n')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) List(out *bytes.Buffer, text func() bool, flags int) {
 | 
					func (options *customHTML) List(out *bytes.Buffer, text func() bool, flags int) {
 | 
				
			||||||
	options.Paragraph(out, text)
 | 
						options.Paragraph(out, text)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (options *customHtml) ListItem(out *bytes.Buffer, text []byte, flags int) {
 | 
					func (options *customHTML) ListItem(out *bytes.Buffer, text []byte, flags int) {
 | 
				
			||||||
	out.WriteString("- ")
 | 
						out.WriteString("- ")
 | 
				
			||||||
	out.Write(text)
 | 
						out.Write(text)
 | 
				
			||||||
	out.WriteByte('\n')
 | 
						out.WriteByte('\n')
 | 
				
			||||||
@@ -53,7 +53,7 @@ func (options *customHtml) ListItem(out *bytes.Buffer, text []byte, flags int) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func makeHTML(input string) string {
 | 
					func makeHTML(input string) string {
 | 
				
			||||||
	return string(blackfriday.Markdown([]byte(input),
 | 
						return string(blackfriday.Markdown([]byte(input),
 | 
				
			||||||
		&customHtml{blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML|blackfriday.HTML_SKIP_IMAGES, "", "")},
 | 
							&customHTML{blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML|blackfriday.HTML_SKIP_IMAGES, "", "")},
 | 
				
			||||||
		blackfriday.EXTENSION_NO_INTRA_EMPHASIS|
 | 
							blackfriday.EXTENSION_NO_INTRA_EMPHASIS|
 | 
				
			||||||
			blackfriday.EXTENSION_FENCED_CODE|
 | 
								blackfriday.EXTENSION_FENCED_CODE|
 | 
				
			||||||
			blackfriday.EXTENSION_AUTOLINK|
 | 
								blackfriday.EXTENSION_AUTOLINK|
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,53 +1,49 @@
 | 
				
			|||||||
package btelegram
 | 
					package btelegram
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"html"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"github.com/go-telegram-bot-api/telegram-bot-api"
 | 
						"github.com/go-telegram-bot-api/telegram-bot-api"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Btelegram struct {
 | 
					type Btelegram struct {
 | 
				
			||||||
	c *tgbotapi.BotAPI
 | 
						c *tgbotapi.BotAPI
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
 | 
						avatarMap map[string]string // keep cache of userid and avatar sha
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "telegram"
 | 
						return &Btelegram{Config: cfg, avatarMap: make(map[string]string)}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Btelegram {
 | 
					 | 
				
			||||||
	return &Btelegram{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) Connect() error {
 | 
					func (b *Btelegram) Connect() error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	flog.Info("Connecting")
 | 
						b.Log.Info("Connecting")
 | 
				
			||||||
	b.c, err = tgbotapi.NewBotAPI(b.Config.Token)
 | 
						b.c, err = tgbotapi.NewBotAPI(b.GetString("Token"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
							b.Log.Debugf("%#v", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	updates, err := b.c.GetUpdatesChan(tgbotapi.NewUpdate(0))
 | 
						u := tgbotapi.NewUpdate(0)
 | 
				
			||||||
 | 
						u.Timeout = 60
 | 
				
			||||||
 | 
						updates, err := b.c.GetUpdatesChan(u)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
							b.Log.Debugf("%#v", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	go b.handleRecv(updates)
 | 
						go b.handleRecv(updates)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) Disconnect() error {
 | 
					func (b *Btelegram) Disconnect() error {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Btelegram) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
@@ -55,16 +51,24 @@ func (b *Btelegram) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
					func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// get the chatid
 | 
				
			||||||
	chatid, err := strconv.ParseInt(msg.Channel, 10, 64)
 | 
						chatid, err := strconv.ParseInt(msg.Channel, 10, 64)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if b.Config.MessageFormat == "HTML" {
 | 
						// map the file SHA to our user (caches the avatar)
 | 
				
			||||||
 | 
						if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
				
			||||||
 | 
							return b.cacheAvatar(&msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if b.GetString("MessageFormat") == "HTML" {
 | 
				
			||||||
		msg.Text = makeHTML(msg.Text)
 | 
							msg.Text = makeHTML(msg.Text)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete message
 | 
				
			||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		if msg.ID == "" {
 | 
							if msg.ID == "" {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
@@ -77,6 +81,17 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Upload a file if it exists
 | 
				
			||||||
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.sendMessage(chatid, rmsg.Username, rmsg.Text)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// check if we have files to upload (from slack, telegram or mattermost)
 | 
				
			||||||
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
 | 
								b.handleUploadFile(&msg, chatid)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// edit the message if we have a msg ID
 | 
						// edit the message if we have a msg ID
 | 
				
			||||||
	if msg.ID != "" {
 | 
						if msg.ID != "" {
 | 
				
			||||||
		msgid, err := strconv.Atoi(msg.ID)
 | 
							msgid, err := strconv.Atoi(msg.ID)
 | 
				
			||||||
@@ -84,9 +99,14 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text)
 | 
							m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text)
 | 
				
			||||||
		if b.Config.MessageFormat == "HTML" {
 | 
							if b.GetString("MessageFormat") == "HTML" {
 | 
				
			||||||
 | 
								b.Log.Debug("Using mode HTML")
 | 
				
			||||||
			m.ParseMode = tgbotapi.ModeHTML
 | 
								m.ParseMode = tgbotapi.ModeHTML
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if b.GetString("MessageFormat") == "Markdown" {
 | 
				
			||||||
 | 
								b.Log.Debug("Using mode markdown")
 | 
				
			||||||
 | 
								m.ParseMode = tgbotapi.ModeMarkdown
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		_, err = b.c.Send(m)
 | 
							_, err = b.c.Send(m)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
@@ -94,98 +114,83 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						// Post normal message
 | 
				
			||||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
						return b.sendMessage(chatid, msg.Username, msg.Text)
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
					 | 
				
			||||||
			var c tgbotapi.Chattable
 | 
					 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
					 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
					 | 
				
			||||||
				file := tgbotapi.FileBytes{fi.Name, *fi.Data}
 | 
					 | 
				
			||||||
				re := regexp.MustCompile(".(jpg|png)$")
 | 
					 | 
				
			||||||
				if re.MatchString(fi.Name) {
 | 
					 | 
				
			||||||
					c = tgbotapi.NewPhotoUpload(chatid, file)
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					c = tgbotapi.NewDocumentUpload(chatid, file)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				_, err := b.c.Send(c)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Errorf("file upload failed: %#v", err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if fi.Comment != "" {
 | 
					 | 
				
			||||||
					b.sendMessage(chatid, msg.Username+fi.Comment)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return "", nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return b.sendMessage(chatid, msg.Username+msg.Text)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
					func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
				
			||||||
	for update := range updates {
 | 
						for update := range updates {
 | 
				
			||||||
		flog.Debugf("Receiving from telegram: %#v", update.Message)
 | 
							b.Log.Debugf("== Receiving event: %#v", update.Message)
 | 
				
			||||||
		var message *tgbotapi.Message
 | 
					 | 
				
			||||||
		username := ""
 | 
					 | 
				
			||||||
		channel := ""
 | 
					 | 
				
			||||||
		text := ""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fmsg := config.Message{Extra: make(map[string][]interface{})}
 | 
							if update.Message == nil && update.ChannelPost == nil {
 | 
				
			||||||
 | 
								b.Log.Error("Getting nil messages, this shouldn't happen.")
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var message *tgbotapi.Message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rmsg := config.Message{Account: b.Account, Extra: make(map[string][]interface{})}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// handle channels
 | 
							// handle channels
 | 
				
			||||||
		if update.ChannelPost != nil {
 | 
							if update.ChannelPost != nil {
 | 
				
			||||||
			message = update.ChannelPost
 | 
								message = update.ChannelPost
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if update.EditedChannelPost != nil && !b.Config.EditDisable {
 | 
					
 | 
				
			||||||
 | 
							// edited channel message
 | 
				
			||||||
 | 
							if update.EditedChannelPost != nil && !b.GetBool("EditDisable") {
 | 
				
			||||||
			message = update.EditedChannelPost
 | 
								message = update.EditedChannelPost
 | 
				
			||||||
			message.Text = message.Text + b.Config.EditSuffix
 | 
								rmsg.Text = rmsg.Text + message.Text + b.GetString("EditSuffix")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// handle groups
 | 
							// handle groups
 | 
				
			||||||
		if update.Message != nil {
 | 
							if update.Message != nil {
 | 
				
			||||||
			message = update.Message
 | 
								message = update.Message
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if update.EditedMessage != nil && !b.Config.EditDisable {
 | 
					
 | 
				
			||||||
 | 
							// edited group message
 | 
				
			||||||
 | 
							if update.EditedMessage != nil && !b.GetBool("EditDisable") {
 | 
				
			||||||
			message = update.EditedMessage
 | 
								message = update.EditedMessage
 | 
				
			||||||
			message.Text = message.Text + b.Config.EditSuffix
 | 
								rmsg.Text = rmsg.Text + message.Text + b.GetString("EditSuffix")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// set the ID's from the channel or group message
 | 
				
			||||||
 | 
							rmsg.ID = strconv.Itoa(message.MessageID)
 | 
				
			||||||
 | 
							rmsg.UserID = strconv.Itoa(message.From.ID)
 | 
				
			||||||
 | 
							rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// handle username
 | 
				
			||||||
		if message.From != nil {
 | 
							if message.From != nil {
 | 
				
			||||||
			if b.Config.UseFirstName {
 | 
								if b.GetBool("UseFirstName") {
 | 
				
			||||||
				username = message.From.FirstName
 | 
									rmsg.Username = message.From.FirstName
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if username == "" {
 | 
								if rmsg.Username == "" {
 | 
				
			||||||
				username = message.From.UserName
 | 
									rmsg.Username = message.From.UserName
 | 
				
			||||||
				if username == "" {
 | 
									if rmsg.Username == "" {
 | 
				
			||||||
					username = message.From.FirstName
 | 
										rmsg.Username = message.From.FirstName
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			text = message.Text
 | 
								rmsg.Text += message.Text
 | 
				
			||||||
			channel = strconv.FormatInt(message.Chat.ID, 10)
 | 
								// only download avatars if we have a place to upload them (configured mediaserver)
 | 
				
			||||||
 | 
								if b.General.MediaServerUpload != "" {
 | 
				
			||||||
 | 
									b.handleDownloadAvatar(message.From.ID, rmsg.Channel)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if username == "" {
 | 
							// if we really didn't find a username, set it to unknown
 | 
				
			||||||
			username = "unknown"
 | 
							if rmsg.Username == "" {
 | 
				
			||||||
		}
 | 
								rmsg.Username = "unknown"
 | 
				
			||||||
		if message.Sticker != nil {
 | 
					 | 
				
			||||||
			b.handleDownload(message.Sticker, &fmsg)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Video != nil {
 | 
					 | 
				
			||||||
			b.handleDownload(message.Video, &fmsg)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Photo != nil {
 | 
					 | 
				
			||||||
			b.handleDownload(message.Photo, &fmsg)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Document != nil {
 | 
					 | 
				
			||||||
			b.handleDownload(message.Document, &fmsg)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Voice != nil {
 | 
					 | 
				
			||||||
			b.handleDownload(message.Voice, &fmsg)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if message.Audio != nil {
 | 
					 | 
				
			||||||
			b.handleDownload(message.Audio, &fmsg)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// handle any downloads
 | 
				
			||||||
 | 
							err := b.handleDownload(message, &rmsg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("download failed: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// handle forwarded messages
 | 
				
			||||||
		if message.ForwardFrom != nil {
 | 
							if message.ForwardFrom != nil {
 | 
				
			||||||
			usernameForward := ""
 | 
								usernameForward := ""
 | 
				
			||||||
			if b.Config.UseFirstName {
 | 
								if b.GetBool("UseFirstName") {
 | 
				
			||||||
				usernameForward = message.ForwardFrom.FirstName
 | 
									usernameForward = message.ForwardFrom.FirstName
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if usernameForward == "" {
 | 
								if usernameForward == "" {
 | 
				
			||||||
@@ -197,14 +202,14 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
				
			|||||||
			if usernameForward == "" {
 | 
								if usernameForward == "" {
 | 
				
			||||||
				usernameForward = "unknown"
 | 
									usernameForward = "unknown"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			text = "Forwarded from " + usernameForward + ": " + text
 | 
								rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// quote the previous message
 | 
							// quote the previous message
 | 
				
			||||||
		if message.ReplyToMessage != nil {
 | 
							if message.ReplyToMessage != nil {
 | 
				
			||||||
			usernameReply := ""
 | 
								usernameReply := ""
 | 
				
			||||||
			if message.ReplyToMessage.From != nil {
 | 
								if message.ReplyToMessage.From != nil {
 | 
				
			||||||
				if b.Config.UseFirstName {
 | 
									if b.GetBool("UseFirstName") {
 | 
				
			||||||
					usernameReply = message.ReplyToMessage.From.FirstName
 | 
										usernameReply = message.ReplyToMessage.From.FirstName
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if usernameReply == "" {
 | 
									if usernameReply == "" {
 | 
				
			||||||
@@ -217,14 +222,15 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
				
			|||||||
			if usernameReply == "" {
 | 
								if usernameReply == "" {
 | 
				
			||||||
				usernameReply = "unknown"
 | 
									usernameReply = "unknown"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			text = text + " (re @" + usernameReply + ":" + message.ReplyToMessage.Text + ")"
 | 
								rmsg.Text = rmsg.Text + " (re @" + usernameReply + ":" + message.ReplyToMessage.Text + ")"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if text != "" || len(fmsg.Extra) > 0 {
 | 
							if rmsg.Text != "" || len(rmsg.Extra) > 0 {
 | 
				
			||||||
			flog.Debugf("Sending message from %s on %s to gateway", username, b.Account)
 | 
								rmsg.Avatar = helper.GetAvatar(b.avatarMap, strconv.Itoa(message.From.ID), b.General)
 | 
				
			||||||
			msg := config.Message{Username: username, Text: text, Channel: channel, Account: b.Account, UserID: strconv.Itoa(message.From.ID), ID: strconv.Itoa(message.MessageID), Extra: fmsg.Extra}
 | 
					
 | 
				
			||||||
			flog.Debugf("Message is %#v", msg)
 | 
								b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
 | 
				
			||||||
			b.Remote <- msg
 | 
								b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
 | 
								b.Remote <- rmsg
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -237,31 +243,46 @@ func (b *Btelegram) getFileDirectURL(id string) string {
 | 
				
			|||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
					// handleDownloadAvatar downloads the avatar of userid from channel
 | 
				
			||||||
	size := 0
 | 
					// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
 | 
				
			||||||
	url := ""
 | 
					// logs an error message if it fails
 | 
				
			||||||
	name := ""
 | 
					func (b *Btelegram) handleDownloadAvatar(userid int, channel string) {
 | 
				
			||||||
	text := ""
 | 
						rmsg := config.Message{Username: "system", Text: "avatar", Channel: channel, Account: b.Account, UserID: strconv.Itoa(userid), Event: config.EVENT_AVATAR_DOWNLOAD, Extra: make(map[string][]interface{})}
 | 
				
			||||||
	fileid := ""
 | 
						if _, ok := b.avatarMap[strconv.Itoa(userid)]; !ok {
 | 
				
			||||||
	switch v := file.(type) {
 | 
							photos, err := b.c.GetUserProfilePhotos(tgbotapi.UserProfilePhotosConfig{UserID: userid, Limit: 1})
 | 
				
			||||||
	case *tgbotapi.Audio:
 | 
							if err != nil {
 | 
				
			||||||
		size = v.FileSize
 | 
								b.Log.Errorf("Userprofile download failed for %#v %s", userid, err)
 | 
				
			||||||
		url = b.getFileDirectURL(v.FileID)
 | 
					 | 
				
			||||||
		urlPart := strings.Split(url, "/")
 | 
					 | 
				
			||||||
		name = urlPart[len(urlPart)-1]
 | 
					 | 
				
			||||||
		text = " " + url
 | 
					 | 
				
			||||||
		fileid = v.FileID
 | 
					 | 
				
			||||||
	case *tgbotapi.Voice:
 | 
					 | 
				
			||||||
		size = v.FileSize
 | 
					 | 
				
			||||||
		url = b.getFileDirectURL(v.FileID)
 | 
					 | 
				
			||||||
		urlPart := strings.Split(url, "/")
 | 
					 | 
				
			||||||
		name = urlPart[len(urlPart)-1]
 | 
					 | 
				
			||||||
		text = " " + url
 | 
					 | 
				
			||||||
		if !strings.HasSuffix(name, ".ogg") {
 | 
					 | 
				
			||||||
			name = name + ".ogg"
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fileid = v.FileID
 | 
					
 | 
				
			||||||
	case *tgbotapi.Sticker:
 | 
							if len(photos.Photos) > 0 {
 | 
				
			||||||
 | 
								photo := photos.Photos[0][0]
 | 
				
			||||||
 | 
								url := b.getFileDirectURL(photo.FileID)
 | 
				
			||||||
 | 
								name := strconv.Itoa(userid) + ".png"
 | 
				
			||||||
 | 
								b.Log.Debugf("trying to download %#v fileid %#v with size %#v", name, photo.FileID, photo.FileSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := helper.HandleDownloadSize(b.Log, &rmsg, name, int64(photo.FileSize), b.General)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									b.Log.Error(err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								data, err := helper.DownloadFile(url)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									b.Log.Errorf("download %s failed %#v", url, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								helper.HandleDownloadData(b.Log, &rmsg, name, rmsg.Text, "", data, b.General)
 | 
				
			||||||
 | 
								b.Remote <- rmsg
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleDownloadFile handles file download
 | 
				
			||||||
 | 
					func (b *Btelegram) handleDownload(message *tgbotapi.Message, rmsg *config.Message) error {
 | 
				
			||||||
 | 
						size := 0
 | 
				
			||||||
 | 
						var url, name, text string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if message.Sticker != nil {
 | 
				
			||||||
 | 
							v := message.Sticker
 | 
				
			||||||
		size = v.FileSize
 | 
							size = v.FileSize
 | 
				
			||||||
		url = b.getFileDirectURL(v.FileID)
 | 
							url = b.getFileDirectURL(v.FileID)
 | 
				
			||||||
		urlPart := strings.Split(url, "/")
 | 
							urlPart := strings.Split(url, "/")
 | 
				
			||||||
@@ -270,54 +291,122 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
				
			|||||||
			name = name + ".webp"
 | 
								name = name + ".webp"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		text = " " + url
 | 
							text = " " + url
 | 
				
			||||||
		fileid = v.FileID
 | 
						}
 | 
				
			||||||
	case *tgbotapi.Video:
 | 
						if message.Video != nil {
 | 
				
			||||||
 | 
							v := message.Video
 | 
				
			||||||
		size = v.FileSize
 | 
							size = v.FileSize
 | 
				
			||||||
		url = b.getFileDirectURL(v.FileID)
 | 
							url = b.getFileDirectURL(v.FileID)
 | 
				
			||||||
		urlPart := strings.Split(url, "/")
 | 
							urlPart := strings.Split(url, "/")
 | 
				
			||||||
		name = urlPart[len(urlPart)-1]
 | 
							name = urlPart[len(urlPart)-1]
 | 
				
			||||||
		text = " " + url
 | 
							text = " " + url
 | 
				
			||||||
		fileid = v.FileID
 | 
						}
 | 
				
			||||||
	case *[]tgbotapi.PhotoSize:
 | 
						if message.Photo != nil {
 | 
				
			||||||
		photos := *v
 | 
							photos := *message.Photo
 | 
				
			||||||
		size = photos[len(photos)-1].FileSize
 | 
							size = photos[len(photos)-1].FileSize
 | 
				
			||||||
		url = b.getFileDirectURL(photos[len(photos)-1].FileID)
 | 
							url = b.getFileDirectURL(photos[len(photos)-1].FileID)
 | 
				
			||||||
		urlPart := strings.Split(url, "/")
 | 
							urlPart := strings.Split(url, "/")
 | 
				
			||||||
		name = urlPart[len(urlPart)-1]
 | 
							name = urlPart[len(urlPart)-1]
 | 
				
			||||||
		text = " " + url
 | 
							text = " " + url
 | 
				
			||||||
	case *tgbotapi.Document:
 | 
						}
 | 
				
			||||||
 | 
						if message.Document != nil {
 | 
				
			||||||
 | 
							v := message.Document
 | 
				
			||||||
		size = v.FileSize
 | 
							size = v.FileSize
 | 
				
			||||||
		url = b.getFileDirectURL(v.FileID)
 | 
							url = b.getFileDirectURL(v.FileID)
 | 
				
			||||||
		name = v.FileName
 | 
							name = v.FileName
 | 
				
			||||||
		text = " " + v.FileName + " : " + url
 | 
							text = " " + v.FileName + " : " + url
 | 
				
			||||||
		fileid = v.FileID
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b.Config.UseInsecureURL {
 | 
						if message.Voice != nil {
 | 
				
			||||||
		msg.Text = text
 | 
							v := message.Voice
 | 
				
			||||||
		return
 | 
							size = v.FileSize
 | 
				
			||||||
	}
 | 
							url = b.getFileDirectURL(v.FileID)
 | 
				
			||||||
	// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
							urlPart := strings.Split(url, "/")
 | 
				
			||||||
	// limit to 1MB for now
 | 
							name = urlPart[len(urlPart)-1]
 | 
				
			||||||
	flog.Debugf("trying to download %#v fileid %#v with size %#v", name, fileid, size)
 | 
							text = " " + url
 | 
				
			||||||
	if size <= b.General.MediaDownloadSize {
 | 
							if !strings.HasSuffix(name, ".ogg") {
 | 
				
			||||||
		data, err := helper.DownloadFile(url)
 | 
								name = name + ".ogg"
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			flog.Errorf("download %s failed %#v", url, err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url))
 | 
					 | 
				
			||||||
			msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: name, Data: data})
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if message.Audio != nil {
 | 
				
			||||||
 | 
							v := message.Audio
 | 
				
			||||||
 | 
							size = v.FileSize
 | 
				
			||||||
 | 
							url = b.getFileDirectURL(v.FileID)
 | 
				
			||||||
 | 
							urlPart := strings.Split(url, "/")
 | 
				
			||||||
 | 
							name = urlPart[len(urlPart)-1]
 | 
				
			||||||
 | 
							text = " " + url
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// if name is empty we didn't match a thing to download
 | 
				
			||||||
 | 
						if name == "" {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// use the URL instead of native upload
 | 
				
			||||||
 | 
						if b.GetBool("UseInsecureURL") {
 | 
				
			||||||
 | 
							b.Log.Debugf("Setting message text to :%s", text)
 | 
				
			||||||
 | 
							rmsg.Text = rmsg.Text + text
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
				
			||||||
 | 
						err := helper.HandleDownloadSize(b.Log, rmsg, name, int64(size), b.General)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						data, err := helper.DownloadFile(url)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, "", data, b.General)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Btelegram) sendMessage(chatid int64, text string) (string, error) {
 | 
					// handleUploadFile handles native upload of files
 | 
				
			||||||
	m := tgbotapi.NewMessage(chatid, text)
 | 
					func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64) (string, error) {
 | 
				
			||||||
	if b.Config.MessageFormat == "HTML" {
 | 
						var c tgbotapi.Chattable
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							file := tgbotapi.FileBytes{fi.Name, *fi.Data}
 | 
				
			||||||
 | 
							re := regexp.MustCompile(".(jpg|png)$")
 | 
				
			||||||
 | 
							if re.MatchString(fi.Name) {
 | 
				
			||||||
 | 
								c = tgbotapi.NewPhotoUpload(chatid, file)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								c = tgbotapi.NewDocumentUpload(chatid, file)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err := b.c.Send(c)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("file upload failed: %#v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if fi.Comment != "" {
 | 
				
			||||||
 | 
								b.sendMessage(chatid, msg.Username, fi.Comment)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, error) {
 | 
				
			||||||
 | 
						m := tgbotapi.NewMessage(chatid, "")
 | 
				
			||||||
 | 
						m.Text = username + text
 | 
				
			||||||
 | 
						if b.GetString("MessageFormat") == "HTML" {
 | 
				
			||||||
 | 
							b.Log.Debug("Using mode HTML")
 | 
				
			||||||
 | 
							username = html.EscapeString(username)
 | 
				
			||||||
 | 
							m.Text = username + text
 | 
				
			||||||
		m.ParseMode = tgbotapi.ModeHTML
 | 
							m.ParseMode = tgbotapi.ModeHTML
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if b.GetString("MessageFormat") == "Markdown" {
 | 
				
			||||||
 | 
							b.Log.Debug("Using mode markdown")
 | 
				
			||||||
 | 
							m.ParseMode = tgbotapi.ModeMarkdown
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	res, err := b.c.Send(m)
 | 
						res, err := b.c.Send(m)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return strconv.Itoa(res.MessageID), nil
 | 
						return strconv.Itoa(res.MessageID), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btelegram) cacheAvatar(msg *config.Message) (string, error) {
 | 
				
			||||||
 | 
						fi := msg.Extra["file"][0].(config.FileInfo)
 | 
				
			||||||
 | 
						/* if we have a sha we have successfully uploaded the file to the media server,
 | 
				
			||||||
 | 
						so we can now cache the sha */
 | 
				
			||||||
 | 
						if fi.SHA != "" {
 | 
				
			||||||
 | 
							b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
 | 
				
			||||||
 | 
							b.avatarMap[msg.UserID] = fi.SHA
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,11 @@ package bxmpp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/jpillora/backoff"
 | 
						"github.com/jpillora/backoff"
 | 
				
			||||||
	"github.com/mattn/go-xmpp"
 | 
						"github.com/matterbridge/go-xmpp"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -14,31 +14,24 @@ import (
 | 
				
			|||||||
type Bxmpp struct {
 | 
					type Bxmpp struct {
 | 
				
			||||||
	xc      *xmpp.Client
 | 
						xc      *xmpp.Client
 | 
				
			||||||
	xmppMap map[string]string
 | 
						xmppMap map[string]string
 | 
				
			||||||
	*config.BridgeConfig
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var flog *log.Entry
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
var protocol = "xmpp"
 | 
						b := &Bxmpp{Config: cfg}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func New(cfg *config.BridgeConfig) *Bxmpp {
 | 
					 | 
				
			||||||
	b := &Bxmpp{BridgeConfig: cfg}
 | 
					 | 
				
			||||||
	b.xmppMap = make(map[string]string)
 | 
						b.xmppMap = make(map[string]string)
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bxmpp) Connect() error {
 | 
					func (b *Bxmpp) Connect() error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	flog.Infof("Connecting %s", b.Config.Server)
 | 
						b.Log.Infof("Connecting %s", b.GetString("Server"))
 | 
				
			||||||
	b.xc, err = b.createXMPP()
 | 
						b.xc, err = b.createXMPP()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
							b.Log.Debugf("%#v", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Info("Connection succeeded")
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		initial := true
 | 
							initial := true
 | 
				
			||||||
		bf := &backoff.Backoff{
 | 
							bf := &backoff.Backoff{
 | 
				
			||||||
@@ -48,16 +41,16 @@ func (b *Bxmpp) Connect() error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		for {
 | 
							for {
 | 
				
			||||||
			if initial {
 | 
								if initial {
 | 
				
			||||||
				b.handleXmpp()
 | 
									b.handleXMPP()
 | 
				
			||||||
				initial = false
 | 
									initial = false
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			d := bf.Duration()
 | 
								d := bf.Duration()
 | 
				
			||||||
			flog.Infof("Disconnected. Reconnecting in %s", d)
 | 
								b.Log.Infof("Disconnected. Reconnecting in %s", d)
 | 
				
			||||||
			time.Sleep(d)
 | 
								time.Sleep(d)
 | 
				
			||||||
			b.xc, err = b.createXMPP()
 | 
								b.xc, err = b.createXMPP()
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
									b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
				
			||||||
				b.handleXmpp()
 | 
									b.handleXMPP()
 | 
				
			||||||
				bf.Reset()
 | 
									bf.Reset()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -70,7 +63,7 @@ func (b *Bxmpp) Disconnect() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
	b.xc.JoinMUCNoHistory(channel.Name+"@"+b.Config.Muc, b.Config.Nick)
 | 
						b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,44 +72,44 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
						if msg.Event == config.EVENT_MSG_DELETE {
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	flog.Debugf("Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)
 | 
				
			||||||
	if msg.Extra != nil {
 | 
						if msg.Extra != nil {
 | 
				
			||||||
 | 
							for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
				
			||||||
 | 
								b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if len(msg.Extra["file"]) > 0 {
 | 
							if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
			for _, f := range msg.Extra["file"] {
 | 
								return b.handleUploadFile(&msg)
 | 
				
			||||||
				fi := f.(config.FileInfo)
 | 
					 | 
				
			||||||
				if fi.URL != "" {
 | 
					 | 
				
			||||||
					msg.Text = fi.URL
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return "", nil
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
						// Post normal message
 | 
				
			||||||
 | 
						_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return "", nil
 | 
						return "", nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
 | 
					func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
 | 
				
			||||||
	tc := new(tls.Config)
 | 
						tc := new(tls.Config)
 | 
				
			||||||
	tc.InsecureSkipVerify = b.Config.SkipTLSVerify
 | 
						tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify")
 | 
				
			||||||
	tc.ServerName = strings.Split(b.Config.Server, ":")[0]
 | 
						tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]
 | 
				
			||||||
	options := xmpp.Options{
 | 
						options := xmpp.Options{
 | 
				
			||||||
		Host:      b.Config.Server,
 | 
							Host:                         b.GetString("Server"),
 | 
				
			||||||
		User:      b.Config.Jid,
 | 
							User:                         b.GetString("Jid"),
 | 
				
			||||||
		Password:  b.Config.Password,
 | 
							Password:                     b.GetString("Password"),
 | 
				
			||||||
		NoTLS:     true,
 | 
							NoTLS:                        true,
 | 
				
			||||||
		StartTLS:  true,
 | 
							StartTLS:                     true,
 | 
				
			||||||
		TLSConfig: tc,
 | 
							TLSConfig:                    tc,
 | 
				
			||||||
 | 
							Debug:                        b.GetBool("debug"),
 | 
				
			||||||
		//StartTLS:      false,
 | 
							Logger:                       b.Log.Writer(),
 | 
				
			||||||
		Debug:                        true,
 | 
					 | 
				
			||||||
		Session:                      true,
 | 
							Session:                      true,
 | 
				
			||||||
		Status:                       "",
 | 
							Status:                       "",
 | 
				
			||||||
		StatusMessage:                "",
 | 
							StatusMessage:                "",
 | 
				
			||||||
		Resource:                     "",
 | 
							Resource:                     "",
 | 
				
			||||||
		InsecureAllowUnencryptedAuth: false,
 | 
							InsecureAllowUnencryptedAuth: false,
 | 
				
			||||||
		//InsecureAllowUnencryptedAuth: true,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	b.xc, err = options.NewClient()
 | 
						b.xc, err = options.NewClient()
 | 
				
			||||||
@@ -131,10 +124,10 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
 | 
				
			|||||||
		for {
 | 
							for {
 | 
				
			||||||
			select {
 | 
								select {
 | 
				
			||||||
			case <-ticker.C:
 | 
								case <-ticker.C:
 | 
				
			||||||
				flog.Debugf("PING")
 | 
									b.Log.Debugf("PING")
 | 
				
			||||||
				err := b.xc.PingC2S("", "")
 | 
									err := b.xc.PingC2S("", "")
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					flog.Debugf("PING failed %#v", err)
 | 
										b.Log.Debugf("PING failed %#v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case <-done:
 | 
								case <-done:
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@@ -144,11 +137,10 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
 | 
				
			|||||||
	return done
 | 
						return done
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bxmpp) handleXmpp() error {
 | 
					func (b *Bxmpp) handleXMPP() error {
 | 
				
			||||||
	var ok bool
 | 
						var ok bool
 | 
				
			||||||
	done := b.xmppKeepAlive()
 | 
						done := b.xmppKeepAlive()
 | 
				
			||||||
	defer close(done)
 | 
						defer close(done)
 | 
				
			||||||
	nodelay := time.Time{}
 | 
					 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		m, err := b.xc.Recv()
 | 
							m, err := b.xc.Recv()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -156,25 +148,22 @@ func (b *Bxmpp) handleXmpp() error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		switch v := m.(type) {
 | 
							switch v := m.(type) {
 | 
				
			||||||
		case xmpp.Chat:
 | 
							case xmpp.Chat:
 | 
				
			||||||
			var channel, nick string
 | 
					 | 
				
			||||||
			if v.Type == "groupchat" {
 | 
								if v.Type == "groupchat" {
 | 
				
			||||||
				s := strings.Split(v.Remote, "@")
 | 
									b.Log.Debugf("== Receiving %#v", v)
 | 
				
			||||||
				if len(s) >= 2 {
 | 
									// skip invalid messages
 | 
				
			||||||
					channel = s[0]
 | 
									if b.skipMessage(v) {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				s = strings.Split(s[1], "/")
 | 
									rmsg := config.Message{Username: b.parseNick(v.Remote), Text: v.Text, Channel: b.parseChannel(v.Remote), Account: b.Account, UserID: v.Remote}
 | 
				
			||||||
				if len(s) == 2 {
 | 
					
 | 
				
			||||||
					nick = s[1]
 | 
									// check if we have an action event
 | 
				
			||||||
				}
 | 
									rmsg.Text, ok = b.replaceAction(rmsg.Text)
 | 
				
			||||||
				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" {
 | 
									if ok {
 | 
				
			||||||
					rmsg := config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote}
 | 
										rmsg.Event = config.EVENT_USER_ACTION
 | 
				
			||||||
					rmsg.Text, ok = b.replaceAction(rmsg.Text)
 | 
					 | 
				
			||||||
					if ok {
 | 
					 | 
				
			||||||
						rmsg.Event = config.EVENT_USER_ACTION
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					flog.Debugf("Sending message from %s on %s to gateway", nick, b.Account)
 | 
					 | 
				
			||||||
					b.Remote <- rmsg
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
 | 
				
			||||||
 | 
									b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
 | 
									b.Remote <- rmsg
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case xmpp.Presence:
 | 
							case xmpp.Presence:
 | 
				
			||||||
			// do nothing
 | 
								// do nothing
 | 
				
			||||||
@@ -188,3 +177,62 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return text, false
 | 
						return text, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleUploadFile handles native upload of files
 | 
				
			||||||
 | 
					func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
 | 
				
			||||||
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
 | 
							if fi.Comment != "" {
 | 
				
			||||||
 | 
								msg.Text += fi.Comment + ": "
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if fi.URL != "" {
 | 
				
			||||||
 | 
								msg.Text += fi.URL
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bxmpp) parseNick(remote string) string {
 | 
				
			||||||
 | 
						s := strings.Split(remote, "@")
 | 
				
			||||||
 | 
						if len(s) > 0 {
 | 
				
			||||||
 | 
							s = strings.Split(s[1], "/")
 | 
				
			||||||
 | 
							if len(s) == 2 {
 | 
				
			||||||
 | 
								return s[1] // nick
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bxmpp) parseChannel(remote string) string {
 | 
				
			||||||
 | 
						s := strings.Split(remote, "@")
 | 
				
			||||||
 | 
						if len(s) >= 2 {
 | 
				
			||||||
 | 
							return s[0] // channel
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// skipMessage skips messages that need to be skipped
 | 
				
			||||||
 | 
					func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
 | 
				
			||||||
 | 
						// skip messages from ourselves
 | 
				
			||||||
 | 
						if b.parseNick(message.Remote) == b.GetString("Nick") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// skip empty messages
 | 
				
			||||||
 | 
						if message.Text == "" {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// skip subject messages
 | 
				
			||||||
 | 
						if strings.Contains(message.Text, "</subject>") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// skip delayed messages
 | 
				
			||||||
 | 
						t := time.Time{}
 | 
				
			||||||
 | 
						return message.Stamp != t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,69 @@
 | 
				
			|||||||
 | 
					# v1.9.0 (the refactor release)
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					* general: better debug messages
 | 
				
			||||||
 | 
					* general: better support for environment variables override
 | 
				
			||||||
 | 
					* general: Ability to disable sending join/leave messages to other gateways. #382
 | 
				
			||||||
 | 
					* slack: Allow Slack @usergroups to be parsed as human-friendly names #379
 | 
				
			||||||
 | 
					* slack: Provide better context for shared posts from Slack<=>Slack enhancement #369
 | 
				
			||||||
 | 
					* telegram: Convert nicks automatically into HTML when MessageFormat is set to HTML #378
 | 
				
			||||||
 | 
					* irc: Add DebugLevel option 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* slack: Ignore restricted_action on channel join (slack). Closes #387
 | 
				
			||||||
 | 
					* slack: Add slack attachment support to matterhook
 | 
				
			||||||
 | 
					* slack: Update userlist on join (slack). Closes #372
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.8.0
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					* general: Send chat notification if media is too big to be re-uploaded to MediaServer. See #359
 | 
				
			||||||
 | 
					* general: Download (and upload) avatar images from mattermost and telegram when mediaserver is configured. Closes #362
 | 
				
			||||||
 | 
					* general: Add label support in RemoteNickFormat
 | 
				
			||||||
 | 
					* general: Prettier info/debug log output
 | 
				
			||||||
 | 
					* mattermost: Download files and reupload to supported bridges (mattermost). Closes #357
 | 
				
			||||||
 | 
					* slack: Add ShowTopicChange option. Allow/disable topic change messages (currently only from slack). Closes #353
 | 
				
			||||||
 | 
					* slack: Add support for file comments (slack). Closes #346
 | 
				
			||||||
 | 
					* telegram: Add comment to file upload from telegram. Show comments on all bridges. Closes #358
 | 
				
			||||||
 | 
					* telegram: Add markdown support (telegram). #355
 | 
				
			||||||
 | 
					* api: Give api access to whole config.Message (and events). Closes #374
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* discord: Check for a valid WebhookURL (discord). Closes #367
 | 
				
			||||||
 | 
					* discord: Fix role mention replace issues
 | 
				
			||||||
 | 
					* irc: Truncate messages sent to IRC based on byte count (#368)
 | 
				
			||||||
 | 
					* mattermost: Add file download urls also to mattermost webhooks #356
 | 
				
			||||||
 | 
					* telegram: Fix panic on nil messages (telegram). Closes #366
 | 
				
			||||||
 | 
					* telegram: Fix the UseInsecureURL text (telegram). Closes #184
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.7.1
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* telegram: Enable Long Polling for Telegram. Reduces bandwidth consumption. (#350)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.7.0
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					* matrix: Add support for deleting messages from/to matrix (matrix). Closes #320
 | 
				
			||||||
 | 
					* xmpp: Ignore <subject> messages (xmpp). #272
 | 
				
			||||||
 | 
					* irc: Add twitch support (irc) to README / wiki
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* general: Change RemoteNickFormat replacement order. Closes #336
 | 
				
			||||||
 | 
					* general: Make edits/delete work for bridges that gets reused. Closes #342
 | 
				
			||||||
 | 
					* general: Lowercase irc channels in config. Closes #348
 | 
				
			||||||
 | 
					* matrix: Fix possible panics (matrix). Closes #333
 | 
				
			||||||
 | 
					* matrix: Add an extension to images without one (matrix). #331
 | 
				
			||||||
 | 
					* api: Obey the Gateway value from the json (api). Closes #344
 | 
				
			||||||
 | 
					* xmpp: Print only debug messages when specified (xmpp). Closes #345
 | 
				
			||||||
 | 
					* xmpp: Allow xmpp to receive the extra messages (file uploads) when text is empty. #295
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.6.3
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* slack: Fix connection issues
 | 
				
			||||||
 | 
					* slack: Add more debug messages
 | 
				
			||||||
 | 
					* irc: Convert received IRC channel names to lowercase. Fixes #329 (#330)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.6.2
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* mattermost: Crashes while connecting to Mattermost (regression). Closes #327
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# v1.6.1
 | 
					# v1.6.1
 | 
				
			||||||
## Bugfix
 | 
					## Bugfix
 | 
				
			||||||
* general: Display of nicks not longer working (regression). Closes #323
 | 
					* general: Display of nicks not longer working (regression). Closes #323
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
go version |grep go1.9 || exit
 | 
					go version |grep go1.10 || exit
 | 
				
			||||||
VERSION=$(git describe --tags)
 | 
					VERSION=$(git describe --tags)
 | 
				
			||||||
mkdir ci/binaries
 | 
					mkdir ci/binaries
 | 
				
			||||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe
 | 
					GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								docker/arm/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								docker/arm/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					FROM cmosh/alpine-arm:edge
 | 
				
			||||||
 | 
					ENTRYPOINT ["/bin/matterbridge"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY . /go/src/github.com/42wim/matterbridge
 | 
				
			||||||
 | 
					RUN apk update && apk add go git gcc musl-dev ca-certificates \
 | 
				
			||||||
 | 
					        && cd /go/src/github.com/42wim/matterbridge \
 | 
				
			||||||
 | 
					        && export GOPATH=/go \
 | 
				
			||||||
 | 
					        && go get \
 | 
				
			||||||
 | 
					        && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
 | 
				
			||||||
 | 
					        && rm -rf /go \
 | 
				
			||||||
 | 
					        && apk del --purge git go gcc musl-dev
 | 
				
			||||||
@@ -4,8 +4,20 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge"
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/api"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						"github.com/42wim/matterbridge/bridge/discord"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/gitter"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/irc"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/matrix"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/mattermost"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/rocketchat"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/slack"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/sshchat"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/steam"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/telegram"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/xmpp"
 | 
				
			||||||
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
	//	"github.com/davecgh/go-spew/spew"
 | 
						//	"github.com/davecgh/go-spew/spew"
 | 
				
			||||||
	"crypto/sha1"
 | 
						"crypto/sha1"
 | 
				
			||||||
	"github.com/hashicorp/golang-lru"
 | 
						"github.com/hashicorp/golang-lru"
 | 
				
			||||||
@@ -29,8 +41,30 @@ type Gateway struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BrMsgID struct {
 | 
					type BrMsgID struct {
 | 
				
			||||||
	br *bridge.Bridge
 | 
						br        *bridge.Bridge
 | 
				
			||||||
	ID string
 | 
						ID        string
 | 
				
			||||||
 | 
						ChannelID string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var flog *log.Entry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var bridgeMap = map[string]bridge.Factory{
 | 
				
			||||||
 | 
						"api":        api.New,
 | 
				
			||||||
 | 
						"discord":    bdiscord.New,
 | 
				
			||||||
 | 
						"gitter":     bgitter.New,
 | 
				
			||||||
 | 
						"irc":        birc.New,
 | 
				
			||||||
 | 
						"mattermost": bmattermost.New,
 | 
				
			||||||
 | 
						"matrix":     bmatrix.New,
 | 
				
			||||||
 | 
						"rocketchat": brocketchat.New,
 | 
				
			||||||
 | 
						"slack":      bslack.New,
 | 
				
			||||||
 | 
						"sshchat":    bsshchat.New,
 | 
				
			||||||
 | 
						"steam":      bsteam.New,
 | 
				
			||||||
 | 
						"telegram":   btelegram.New,
 | 
				
			||||||
 | 
						"xmpp":       bxmpp.New,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						flog = log.WithFields(log.Fields{"prefix": "gateway"})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg config.Gateway, r *Router) *Gateway {
 | 
					func New(cfg config.Gateway, r *Router) *Gateway {
 | 
				
			||||||
@@ -45,7 +79,14 @@ func New(cfg config.Gateway, r *Router) *Gateway {
 | 
				
			|||||||
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
 | 
					func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
 | 
				
			||||||
	br := gw.Router.getBridge(cfg.Account)
 | 
						br := gw.Router.getBridge(cfg.Account)
 | 
				
			||||||
	if br == nil {
 | 
						if br == nil {
 | 
				
			||||||
		br = bridge.New(gw.Config, cfg, gw.Message)
 | 
							br = bridge.New(cfg)
 | 
				
			||||||
 | 
							br.Config = gw.Router.Config
 | 
				
			||||||
 | 
							br.General = &gw.General
 | 
				
			||||||
 | 
							// set logging
 | 
				
			||||||
 | 
							br.Log = log.WithFields(log.Fields{"prefix": "bridge"})
 | 
				
			||||||
 | 
							brconfig := &bridge.Config{Remote: gw.Message, Log: log.WithFields(log.Fields{"prefix": br.Protocol}), Bridge: br}
 | 
				
			||||||
 | 
							// add the actual bridger for this protocol to this bridge using the bridgeMap
 | 
				
			||||||
 | 
							br.Bridger = bridgeMap[br.Protocol](brconfig)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gw.mapChannelsToBridge(br)
 | 
						gw.mapChannelsToBridge(br)
 | 
				
			||||||
	gw.Bridges[cfg.Account] = br
 | 
						gw.Bridges[cfg.Account] = br
 | 
				
			||||||
@@ -77,10 +118,10 @@ func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
 | 
				
			|||||||
	br.Disconnect()
 | 
						br.Disconnect()
 | 
				
			||||||
	time.Sleep(time.Second * 5)
 | 
						time.Sleep(time.Second * 5)
 | 
				
			||||||
RECONNECT:
 | 
					RECONNECT:
 | 
				
			||||||
	log.Infof("Reconnecting %s", br.Account)
 | 
						flog.Infof("Reconnecting %s", br.Account)
 | 
				
			||||||
	err := br.Connect()
 | 
						err := br.Connect()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
 | 
							flog.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
 | 
				
			||||||
		time.Sleep(time.Second * 60)
 | 
							time.Sleep(time.Second * 60)
 | 
				
			||||||
		goto RECONNECT
 | 
							goto RECONNECT
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -93,6 +134,10 @@ func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
 | 
				
			|||||||
		if isApi(br.Account) {
 | 
							if isApi(br.Account) {
 | 
				
			||||||
			br.Channel = "api"
 | 
								br.Channel = "api"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// make sure to lowercase irc channels in config #348
 | 
				
			||||||
 | 
							if strings.HasPrefix(br.Account, "irc.") {
 | 
				
			||||||
 | 
								br.Channel = strings.ToLower(br.Channel)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ID := br.Channel + br.Account
 | 
							ID := br.Channel + br.Account
 | 
				
			||||||
		if _, ok := gw.Channels[ID]; !ok {
 | 
							if _, ok := gw.Channels[ID]; !ok {
 | 
				
			||||||
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
 | 
								channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
 | 
				
			||||||
@@ -118,6 +163,12 @@ func (gw *Gateway) mapChannels() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
 | 
					func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
 | 
				
			||||||
	var channels []config.ChannelInfo
 | 
						var channels []config.ChannelInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// for messages received from the api check that the gateway is the specified one
 | 
				
			||||||
 | 
						if msg.Protocol == "api" && gw.Name != msg.Gateway {
 | 
				
			||||||
 | 
							return channels
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// if source channel is in only, do nothing
 | 
						// if source channel is in only, do nothing
 | 
				
			||||||
	for _, channel := range gw.Channels {
 | 
						for _, channel := range gw.Channels {
 | 
				
			||||||
		// lookup the channel from the message
 | 
							// lookup the channel from the message
 | 
				
			||||||
@@ -134,7 +185,7 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// do samechannelgateway logic
 | 
							// do samechannelgateway flogic
 | 
				
			||||||
		if channel.SameChannel[msg.Gateway] {
 | 
							if channel.SameChannel[msg.Gateway] {
 | 
				
			||||||
			if msg.Channel == channel.Name && msg.Account != dest.Account {
 | 
								if msg.Channel == channel.Name && msg.Account != dest.Account {
 | 
				
			||||||
				channels = append(channels, *channel)
 | 
									channels = append(channels, *channel)
 | 
				
			||||||
@@ -151,38 +202,55 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
 | 
				
			|||||||
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
 | 
					func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
 | 
				
			||||||
	var brMsgIDs []*BrMsgID
 | 
						var brMsgIDs []*BrMsgID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO refactor
 | 
						// if we have an attached file, or other info
 | 
				
			||||||
	// only slack now, check will have to be done in the different bridges.
 | 
					 | 
				
			||||||
	// we need to check if we can't use fallback or text in other bridges
 | 
					 | 
				
			||||||
	if msg.Extra != nil {
 | 
						if msg.Extra != nil {
 | 
				
			||||||
		if dest.Protocol != "discord" &&
 | 
							if len(msg.Extra[config.EVENT_FILE_FAILURE_SIZE]) != 0 {
 | 
				
			||||||
			dest.Protocol != "slack" &&
 | 
					 | 
				
			||||||
			dest.Protocol != "mattermost" &&
 | 
					 | 
				
			||||||
			dest.Protocol != "telegram" &&
 | 
					 | 
				
			||||||
			dest.Protocol != "matrix" {
 | 
					 | 
				
			||||||
			if msg.Text == "" {
 | 
								if msg.Text == "" {
 | 
				
			||||||
				return brMsgIDs
 | 
									return brMsgIDs
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// only relay join/part when configged
 | 
					
 | 
				
			||||||
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
 | 
						// Avatar downloads are only relevant for telegram and mattermost for now
 | 
				
			||||||
 | 
						if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
				
			||||||
 | 
							if dest.Protocol != "mattermost" &&
 | 
				
			||||||
 | 
								dest.Protocol != "telegram" {
 | 
				
			||||||
 | 
								return brMsgIDs
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// only relay join/part when configured
 | 
				
			||||||
 | 
						if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].GetBool("ShowJoinPart") {
 | 
				
			||||||
		return brMsgIDs
 | 
							return brMsgIDs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// only relay topic change when configured
 | 
				
			||||||
 | 
						if msg.Event == config.EVENT_TOPIC_CHANGE && !gw.Bridges[dest.Account].GetBool("ShowTopicChange") {
 | 
				
			||||||
 | 
							return brMsgIDs
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// broadcast to every out channel (irc QUIT)
 | 
						// broadcast to every out channel (irc QUIT)
 | 
				
			||||||
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
 | 
						if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
 | 
				
			||||||
		log.Debug("empty channel")
 | 
							flog.Debug("empty channel")
 | 
				
			||||||
		return brMsgIDs
 | 
							return brMsgIDs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	originchannel := msg.Channel
 | 
						originchannel := msg.Channel
 | 
				
			||||||
	origmsg := msg
 | 
						origmsg := msg
 | 
				
			||||||
	channels := gw.getDestChannel(&msg, *dest)
 | 
						channels := gw.getDestChannel(&msg, *dest)
 | 
				
			||||||
	for _, channel := range channels {
 | 
						for _, channel := range channels {
 | 
				
			||||||
		// do not send to ourself
 | 
							// Only send the avatar download event to ourselves.
 | 
				
			||||||
		if channel.ID == getChannelID(origmsg) {
 | 
							if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
				
			||||||
			continue
 | 
								if channel.ID != getChannelID(origmsg) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// do not send to ourself for any other event
 | 
				
			||||||
 | 
								if channel.ID == getChannelID(origmsg) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
							flog.Debugf("=> Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
				
			||||||
		msg.Channel = channel.Name
 | 
							msg.Channel = channel.Name
 | 
				
			||||||
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
 | 
							msg.Avatar = gw.modifyAvatar(origmsg, dest)
 | 
				
			||||||
		msg.Username = gw.modifyUsername(origmsg, dest)
 | 
							msg.Username = gw.modifyUsername(origmsg, dest)
 | 
				
			||||||
@@ -190,7 +258,9 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
				
			|||||||
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
 | 
							if res, ok := gw.Messages.Get(origmsg.ID); ok {
 | 
				
			||||||
			IDs := res.([]*BrMsgID)
 | 
								IDs := res.([]*BrMsgID)
 | 
				
			||||||
			for _, id := range IDs {
 | 
								for _, id := range IDs {
 | 
				
			||||||
				if dest.Protocol == id.br.Protocol {
 | 
									// check protocol, bridge name and channelname
 | 
				
			||||||
 | 
									// for people that reuse the same bridge multiple times. see #342
 | 
				
			||||||
 | 
									if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
 | 
				
			||||||
					msg.ID = id.ID
 | 
										msg.ID = id.ID
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -201,11 +271,12 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		mID, err := dest.Send(msg)
 | 
							mID, err := dest.Send(msg)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			fmt.Println(err)
 | 
								flog.Error(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
 | 
							// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
 | 
				
			||||||
		if mID != "" {
 | 
							if mID != "" {
 | 
				
			||||||
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID})
 | 
								flog.Debugf("mID %s: %s", dest.Account, mID)
 | 
				
			||||||
 | 
								brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return brMsgIDs
 | 
						return brMsgIDs
 | 
				
			||||||
@@ -216,30 +287,39 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
 | 
				
			|||||||
	if _, ok := gw.Bridges[msg.Account]; !ok {
 | 
						if _, ok := gw.Bridges[msg.Account]; !ok {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if we need to ignore a empty message
 | 
				
			||||||
	if msg.Text == "" {
 | 
						if msg.Text == "" {
 | 
				
			||||||
		// we have an attachment or actual bytes
 | 
							// we have an attachment or actual bytes, do not ignore
 | 
				
			||||||
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
 | 
							if msg.Extra != nil &&
 | 
				
			||||||
 | 
								(msg.Extra["attachments"] != nil ||
 | 
				
			||||||
 | 
									len(msg.Extra["file"]) > 0 ||
 | 
				
			||||||
 | 
									len(msg.Extra[config.EVENT_FILE_FAILURE_SIZE]) > 0) {
 | 
				
			||||||
			return false
 | 
								return false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
 | 
							flog.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
 | 
					
 | 
				
			||||||
 | 
						// is the username in IgnoreNicks field
 | 
				
			||||||
 | 
						for _, entry := range strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreNicks")) {
 | 
				
			||||||
		if msg.Username == entry {
 | 
							if msg.Username == entry {
 | 
				
			||||||
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
 | 
								flog.Debugf("ignoring %s from %s", msg.Username, msg.Account)
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// does the message match regex in IgnoreMessages field
 | 
				
			||||||
	// TODO do not compile regexps everytime
 | 
						// TODO do not compile regexps everytime
 | 
				
			||||||
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
 | 
						for _, entry := range strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreMessages")) {
 | 
				
			||||||
		if entry != "" {
 | 
							if entry != "" {
 | 
				
			||||||
			re, err := regexp.Compile(entry)
 | 
								re, err := regexp.Compile(entry)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
 | 
									flog.Errorf("incorrect regexp %s for %s", entry, msg.Account)
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if re.MatchString(msg.Text) {
 | 
								if re.MatchString(msg.Text) {
 | 
				
			||||||
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
 | 
									flog.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
 | 
				
			||||||
				return true
 | 
									return true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -250,23 +330,23 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
 | 
				
			|||||||
func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
 | 
					func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
 | 
				
			||||||
	br := gw.Bridges[msg.Account]
 | 
						br := gw.Bridges[msg.Account]
 | 
				
			||||||
	msg.Protocol = br.Protocol
 | 
						msg.Protocol = br.Protocol
 | 
				
			||||||
	if gw.Config.General.StripNick || dest.Config.StripNick {
 | 
						if gw.Config.General.StripNick || dest.GetBool("StripNick") {
 | 
				
			||||||
		re := regexp.MustCompile("[^a-zA-Z0-9]+")
 | 
							re := regexp.MustCompile("[^a-zA-Z0-9]+")
 | 
				
			||||||
		msg.Username = re.ReplaceAllString(msg.Username, "")
 | 
							msg.Username = re.ReplaceAllString(msg.Username, "")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nick := dest.Config.RemoteNickFormat
 | 
						nick := dest.GetString("RemoteNickFormat")
 | 
				
			||||||
	if nick == "" {
 | 
						if nick == "" {
 | 
				
			||||||
		nick = gw.Config.General.RemoteNickFormat
 | 
							nick = gw.Config.General.RemoteNickFormat
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// loop to replace nicks
 | 
						// loop to replace nicks
 | 
				
			||||||
	for _, outer := range br.Config.ReplaceNicks {
 | 
						for _, outer := range br.GetStringSlice2D("ReplaceNicks") {
 | 
				
			||||||
		search := outer[0]
 | 
							search := outer[0]
 | 
				
			||||||
		replace := outer[1]
 | 
							replace := outer[1]
 | 
				
			||||||
		// TODO move compile to bridge init somewhere
 | 
							// TODO move compile to bridge init somewhere
 | 
				
			||||||
		re, err := regexp.Compile(search)
 | 
							re, err := regexp.Compile(search)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
								flog.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		msg.Username = re.ReplaceAllString(msg.Username, replace)
 | 
							msg.Username = re.ReplaceAllString(msg.Username, replace)
 | 
				
			||||||
@@ -284,16 +364,18 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
 | 
							nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
					
 | 
				
			||||||
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
 | 
						nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
 | 
				
			||||||
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
 | 
						nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
 | 
				
			||||||
 | 
						nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1)
 | 
				
			||||||
 | 
						nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
				
			||||||
	return nick
 | 
						return nick
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
 | 
					func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
 | 
				
			||||||
	iconurl := gw.Config.General.IconURL
 | 
						iconurl := gw.Config.General.IconURL
 | 
				
			||||||
	if iconurl == "" {
 | 
						if iconurl == "" {
 | 
				
			||||||
		iconurl = dest.Config.IconURL
 | 
							iconurl = dest.GetString("IconURL")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
 | 
						iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
 | 
				
			||||||
	if msg.Avatar == "" {
 | 
						if msg.Avatar == "" {
 | 
				
			||||||
@@ -305,26 +387,34 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string
 | 
				
			|||||||
func (gw *Gateway) modifyMessage(msg *config.Message) {
 | 
					func (gw *Gateway) modifyMessage(msg *config.Message) {
 | 
				
			||||||
	// replace :emoji: to unicode
 | 
						// replace :emoji: to unicode
 | 
				
			||||||
	msg.Text = emojilib.Replace(msg.Text)
 | 
						msg.Text = emojilib.Replace(msg.Text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	br := gw.Bridges[msg.Account]
 | 
						br := gw.Bridges[msg.Account]
 | 
				
			||||||
	// loop to replace messages
 | 
						// loop to replace messages
 | 
				
			||||||
	for _, outer := range br.Config.ReplaceMessages {
 | 
						for _, outer := range br.GetStringSlice2D("ReplaceMessages") {
 | 
				
			||||||
		search := outer[0]
 | 
							search := outer[0]
 | 
				
			||||||
		replace := outer[1]
 | 
							replace := outer[1]
 | 
				
			||||||
		// TODO move compile to bridge init somewhere
 | 
							// TODO move compile to bridge init somewhere
 | 
				
			||||||
		re, err := regexp.Compile(search)
 | 
							re, err := regexp.Compile(search)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
								flog.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		msg.Text = re.ReplaceAllString(msg.Text, replace)
 | 
							msg.Text = re.ReplaceAllString(msg.Text, replace)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	msg.Gateway = gw.Name
 | 
					
 | 
				
			||||||
 | 
						// messages from api have Gateway specified, don't overwrite
 | 
				
			||||||
 | 
						if msg.Protocol != "api" {
 | 
				
			||||||
 | 
							msg.Gateway = gw.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
					func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
				
			||||||
 | 
						// if we don't have a attachfield or we don't have a mediaserver configured return
 | 
				
			||||||
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
 | 
						if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if we actually have files, start uploading them to the mediaserver
 | 
				
			||||||
	if len(msg.Extra["file"]) > 0 {
 | 
						if len(msg.Extra["file"]) > 0 {
 | 
				
			||||||
		client := &http.Client{
 | 
							client := &http.Client{
 | 
				
			||||||
			Timeout: time.Second * 5,
 | 
								Timeout: time.Second * 5,
 | 
				
			||||||
@@ -337,26 +427,29 @@ func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
				
			|||||||
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
 | 
								durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
 | 
				
			||||||
			extra := msg.Extra["file"][i].(config.FileInfo)
 | 
								extra := msg.Extra["file"][i].(config.FileInfo)
 | 
				
			||||||
			extra.URL = durl
 | 
								extra.URL = durl
 | 
				
			||||||
			msg.Extra["file"][i] = extra
 | 
					 | 
				
			||||||
			req, _ := http.NewRequest("PUT", url, reader)
 | 
								req, _ := http.NewRequest("PUT", url, reader)
 | 
				
			||||||
			req.Header.Set("Content-Type", "binary/octet-stream")
 | 
								req.Header.Set("Content-Type", "binary/octet-stream")
 | 
				
			||||||
			_, err := client.Do(req)
 | 
								_, err := client.Do(req)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Errorf("mediaserver upload failed: %#v", err)
 | 
									flog.Errorf("mediaserver upload failed: %#v", err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			log.Debugf("mediaserver download URL = %s", durl)
 | 
								flog.Debugf("mediaserver download URL = %s", durl)
 | 
				
			||||||
 | 
								// we uploaded the file successfully. Add the SHA
 | 
				
			||||||
 | 
								extra.SHA = sha1sum
 | 
				
			||||||
 | 
								msg.Extra["file"][i] = extra
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getChannelID(msg config.Message) string {
 | 
					 | 
				
			||||||
	return msg.Channel + msg.Account
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
 | 
					func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
 | 
				
			||||||
	return msg.Gateway == gw.Name
 | 
						return msg.Gateway == gw.Name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getChannelID(msg config.Message) string {
 | 
				
			||||||
 | 
						return msg.Channel + msg.Account
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isApi(account string) bool {
 | 
					func isApi(account string) bool {
 | 
				
			||||||
	return strings.HasPrefix(account, "api.")
 | 
						return strings.HasPrefix(account, "api.")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,14 +3,13 @@ package gateway
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/BurntSushi/toml"
 | 
					 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var testconfig = `
 | 
					var testconfig = []byte(`
 | 
				
			||||||
[irc.freenode]
 | 
					[irc.freenode]
 | 
				
			||||||
[mattermost.test]
 | 
					[mattermost.test]
 | 
				
			||||||
[gitter.42wim]
 | 
					[gitter.42wim]
 | 
				
			||||||
@@ -37,9 +36,9 @@ var testconfig = `
 | 
				
			|||||||
    [[gateway.inout]]
 | 
					    [[gateway.inout]]
 | 
				
			||||||
    account="slack.test"
 | 
					    account="slack.test"
 | 
				
			||||||
    channel="testing"
 | 
					    channel="testing"
 | 
				
			||||||
	`
 | 
						`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var testconfig2 = `
 | 
					var testconfig2 = []byte(`
 | 
				
			||||||
[irc.freenode]
 | 
					[irc.freenode]
 | 
				
			||||||
[mattermost.test]
 | 
					[mattermost.test]
 | 
				
			||||||
[gitter.42wim]
 | 
					[gitter.42wim]
 | 
				
			||||||
@@ -80,8 +79,9 @@ var testconfig2 = `
 | 
				
			|||||||
    [[gateway.out]]
 | 
					    [[gateway.out]]
 | 
				
			||||||
    account = "discord.test"
 | 
					    account = "discord.test"
 | 
				
			||||||
    channel = "general2"
 | 
					    channel = "general2"
 | 
				
			||||||
	`
 | 
						`)
 | 
				
			||||||
var testconfig3 = `
 | 
					
 | 
				
			||||||
 | 
					var testconfig3 = []byte(`
 | 
				
			||||||
[irc.zzz]
 | 
					[irc.zzz]
 | 
				
			||||||
[telegram.zzz]
 | 
					[telegram.zzz]
 | 
				
			||||||
[slack.zzz]
 | 
					[slack.zzz]
 | 
				
			||||||
@@ -149,13 +149,10 @@ enable=true
 | 
				
			|||||||
    [[gateway.inout]]
 | 
					    [[gateway.inout]]
 | 
				
			||||||
    account="telegram.zzz"
 | 
					    account="telegram.zzz"
 | 
				
			||||||
    channel="--333333333333"
 | 
					    channel="--333333333333"
 | 
				
			||||||
`
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func maketestRouter(input string) *Router {
 | 
					func maketestRouter(input []byte) *Router {
 | 
				
			||||||
	var cfg *config.Config
 | 
						cfg := config.NewConfigFromString(input)
 | 
				
			||||||
	if _, err := toml.Decode(input, &cfg); err != nil {
 | 
					 | 
				
			||||||
		fmt.Println(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	r, err := NewRouter(cfg)
 | 
						r, err := NewRouter(cfg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println(err)
 | 
							fmt.Println(err)
 | 
				
			||||||
@@ -163,14 +160,7 @@ func maketestRouter(input string) *Router {
 | 
				
			|||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
func TestNewRouter(t *testing.T) {
 | 
					func TestNewRouter(t *testing.T) {
 | 
				
			||||||
	var cfg *config.Config
 | 
						r := maketestRouter(testconfig)
 | 
				
			||||||
	if _, err := toml.Decode(testconfig, &cfg); err != nil {
 | 
					 | 
				
			||||||
		fmt.Println(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	r, err := NewRouter(cfg)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Println(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	assert.Equal(t, 1, len(r.Gateways))
 | 
						assert.Equal(t, 1, len(r.Gateways))
 | 
				
			||||||
	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))
 | 
						assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))
 | 
				
			||||||
	assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels))
 | 
						assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ import (
 | 
				
			|||||||
	"github.com/42wim/matterbridge/bridge"
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/42wim/matterbridge/gateway/samechannel"
 | 
						"github.com/42wim/matterbridge/gateway/samechannel"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	//	"github.com/davecgh/go-spew/spew"
 | 
						//	"github.com/davecgh/go-spew/spew"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -17,10 +16,7 @@ type Router struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewRouter(cfg *config.Config) (*Router, error) {
 | 
					func NewRouter(cfg *config.Config) (*Router, error) {
 | 
				
			||||||
	r := &Router{}
 | 
						r := &Router{Message: make(chan config.Message), Gateways: make(map[string]*Gateway), Config: cfg}
 | 
				
			||||||
	r.Config = cfg
 | 
					 | 
				
			||||||
	r.Message = make(chan config.Message)
 | 
					 | 
				
			||||||
	r.Gateways = make(map[string]*Gateway)
 | 
					 | 
				
			||||||
	sgw := samechannelgateway.New(cfg)
 | 
						sgw := samechannelgateway.New(cfg)
 | 
				
			||||||
	gwconfigs := sgw.GetConfig()
 | 
						gwconfigs := sgw.GetConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,12 +38,13 @@ func NewRouter(cfg *config.Config) (*Router, error) {
 | 
				
			|||||||
func (r *Router) Start() error {
 | 
					func (r *Router) Start() error {
 | 
				
			||||||
	m := make(map[string]*bridge.Bridge)
 | 
						m := make(map[string]*bridge.Bridge)
 | 
				
			||||||
	for _, gw := range r.Gateways {
 | 
						for _, gw := range r.Gateways {
 | 
				
			||||||
 | 
							flog.Infof("Parsing gateway %s", gw.Name)
 | 
				
			||||||
		for _, br := range gw.Bridges {
 | 
							for _, br := range gw.Bridges {
 | 
				
			||||||
			m[br.Account] = br
 | 
								m[br.Account] = br
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, br := range m {
 | 
						for _, br := range m {
 | 
				
			||||||
		log.Infof("Starting bridge: %s ", br.Account)
 | 
							flog.Infof("Starting bridge: %s ", br.Account)
 | 
				
			||||||
		err := br.Connect()
 | 
							err := br.Connect()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
 | 
								return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,22 +5,21 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/42wim/matterbridge/gateway"
 | 
						"github.com/42wim/matterbridge/gateway"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"github.com/google/gops/agent"
 | 
						"github.com/google/gops/agent"
 | 
				
			||||||
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						prefixed "github.com/x-cray/logrus-prefixed-formatter"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	version = "1.6.1"
 | 
						version = "1.9.0"
 | 
				
			||||||
	githash string
 | 
						githash string
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
						log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: true})
 | 
				
			||||||
 | 
						flog := log.WithFields(log.Fields{"prefix": "main"})
 | 
				
			||||||
	flagConfig := flag.String("conf", "matterbridge.toml", "config file")
 | 
						flagConfig := flag.String("conf", "matterbridge.toml", "config file")
 | 
				
			||||||
	flagDebug := flag.Bool("debug", false, "enable debug")
 | 
						flagDebug := flag.Bool("debug", false, "enable debug")
 | 
				
			||||||
	flagVersion := flag.Bool("version", false, "show version")
 | 
						flagVersion := flag.Bool("version", false, "show version")
 | 
				
			||||||
@@ -35,22 +34,24 @@ func main() {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if *flagDebug || os.Getenv("DEBUG") == "1" {
 | 
						if *flagDebug || os.Getenv("DEBUG") == "1" {
 | 
				
			||||||
		log.Info("Enabling debug")
 | 
							log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: false, ForceFormatting: true})
 | 
				
			||||||
 | 
							flog.Info("Enabling debug")
 | 
				
			||||||
		log.SetLevel(log.DebugLevel)
 | 
							log.SetLevel(log.DebugLevel)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.Printf("Running version %s %s", version, githash)
 | 
						flog.Printf("Running version %s %s", version, githash)
 | 
				
			||||||
	if strings.Contains(version, "-dev") {
 | 
						if strings.Contains(version, "-dev") {
 | 
				
			||||||
		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
 | 
							flog.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cfg := config.NewConfig(*flagConfig)
 | 
						cfg := config.NewConfig(*flagConfig)
 | 
				
			||||||
 | 
						cfg.General.Debug = *flagDebug
 | 
				
			||||||
	r, err := gateway.NewRouter(cfg)
 | 
						r, err := gateway.NewRouter(cfg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("Starting gateway failed: %s", err)
 | 
							flog.Fatalf("Starting gateway failed: %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = r.Start()
 | 
						err = r.Start()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("Starting gateway failed: %s", err)
 | 
							flog.Fatalf("Starting gateway failed: %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.Printf("Gateway(s) started succesfully. Now relaying messages")
 | 
						flog.Printf("Gateway(s) started succesfully. Now relaying messages")
 | 
				
			||||||
	select {}
 | 
						select {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -117,24 +117,39 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#The string "{NOPINGNICK}" (case sensitive) will be replaced by the actual nick / username, but with a ZWSP inside the nick, so the irc user with the same nick won't get pinged. See https://github.com/42wim/matterbridge/issues/175 for more information
 | 
					#The string "{NOPINGNICK}" (case sensitive) will be replaced by the actual nick / username, but with a ZWSP inside the nick, so the irc user with the same nick won't get pinged. See https://github.com/42wim/matterbridge/issues/175 for more information
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Do not send joins/parts to other bridges
 | 
				
			||||||
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					NoSendJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
					#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
				
			||||||
#It will strip other characters from the nick
 | 
					#It will strip other characters from the nick
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#XMPP section
 | 
					#XMPP section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -197,15 +212,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -214,6 +234,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#hipchat section
 | 
					#hipchat section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -268,15 +293,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -285,6 +315,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#mattermost section
 | 
					#mattermost section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -399,23 +434,38 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Do not send joins/parts to other bridges
 | 
				
			||||||
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					NoSendJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
					#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
				
			||||||
#It will strip other characters from the nick
 | 
					#It will strip other characters from the nick
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#Gitter section
 | 
					#Gitter section
 | 
				
			||||||
#Best to make a dedicated gitter account for the bot.
 | 
					#Best to make a dedicated gitter account for the bot.
 | 
				
			||||||
@@ -460,15 +510,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -477,6 +532,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#slack section
 | 
					#slack section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -512,6 +572,7 @@ WebhookBindAddress="0.0.0.0:9999"
 | 
				
			|||||||
#Icon that will be showed in slack
 | 
					#Icon that will be showed in slack
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL
 | 
					#OPTIONAL
 | 
				
			||||||
IconURL="https://robohash.org/{NICK}.png?size=48x48"
 | 
					IconURL="https://robohash.org/{NICK}.png?size=48x48"
 | 
				
			||||||
@@ -568,23 +629,38 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Do not send joins/parts to other bridges
 | 
				
			||||||
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					NoSendJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
					#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
				
			||||||
#It will strip other characters from the nick
 | 
					#It will strip other characters from the nick
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#discord section
 | 
					#discord section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -653,15 +729,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -670,6 +751,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#telegram section
 | 
					#telegram section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -737,15 +823,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -754,6 +845,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#rocketchat section
 | 
					#rocketchat section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -822,15 +918,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -839,6 +940,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#matrix section
 | 
					#matrix section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -899,15 +1005,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -916,6 +1027,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#steam section
 | 
					#steam section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -970,15 +1086,20 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			|||||||
#optional (default empty)
 | 
					#optional (default empty)
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -987,6 +1108,11 @@ ShowJoinPart=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#API
 | 
					#API
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -1008,9 +1134,14 @@ Buffer=1000
 | 
				
			|||||||
#OPTIONAL (no authorization if token is empty)
 | 
					#OPTIONAL (no authorization if token is empty)
 | 
				
			||||||
Token="mytoken"
 | 
					Token="mytoken"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="{NICK}"
 | 
					RemoteNickFormat="{NICK}"
 | 
				
			||||||
@@ -1025,6 +1156,7 @@ RemoteNickFormat="{NICK}"
 | 
				
			|||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					#RemoteNickFormat defines how remote users appear on this bridge 
 | 
				
			||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
					#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
				
			||||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
					#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
				
			||||||
 | 
					#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
				
			||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
					#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
@@ -1053,7 +1185,7 @@ MediaServerDownload="https://youserver.com/download"
 | 
				
			|||||||
#eg downloading from slack to upload it to mattermost
 | 
					#eg downloading from slack to upload it to mattermost
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#It will only download from bridges that don't have public links available, which are for the moment
 | 
					#It will only download from bridges that don't have public links available, which are for the moment
 | 
				
			||||||
#slack, telegram and matrix
 | 
					#slack, telegram, matrix and mattermost
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#Optional (default 1000000 (1 megabyte))
 | 
					#Optional (default 1000000 (1 megabyte))
 | 
				
			||||||
MediaDownloadSize=1000000
 | 
					MediaDownloadSize=1000000
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,8 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						prefixed "github.com/x-cray/logrus-prefixed-formatter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/websocket"
 | 
						"github.com/gorilla/websocket"
 | 
				
			||||||
	"github.com/hashicorp/golang-lru"
 | 
						"github.com/hashicorp/golang-lru"
 | 
				
			||||||
@@ -73,12 +74,16 @@ type MMClient struct {
 | 
				
			|||||||
func New(login, pass, team, server string) *MMClient {
 | 
					func New(login, pass, team, server string) *MMClient {
 | 
				
			||||||
	cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
 | 
						cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
 | 
				
			||||||
	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)}
 | 
						mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)}
 | 
				
			||||||
	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"})
 | 
						log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true})
 | 
				
			||||||
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
 | 
						mmclient.log = log.WithFields(log.Fields{"prefix": "matterclient"})
 | 
				
			||||||
	mmclient.lruCache, _ = lru.New(500)
 | 
						mmclient.lruCache, _ = lru.New(500)
 | 
				
			||||||
	return mmclient
 | 
						return mmclient
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *MMClient) SetDebugLog() {
 | 
				
			||||||
 | 
						log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: false, ForceFormatting: true})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) SetLogLevel(level string) {
 | 
					func (m *MMClient) SetLogLevel(level string) {
 | 
				
			||||||
	l, err := log.ParseLevel(level)
 | 
						l, err := log.ParseLevel(level)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -585,9 +590,9 @@ func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
 | 
				
			|||||||
func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
					func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
				
			||||||
	m.log.Debugf("posting lastview %#v", channelId)
 | 
						m.log.Debugf("posting lastview %#v", channelId)
 | 
				
			||||||
	view := &model.ChannelView{ChannelId: channelId}
 | 
						view := &model.ChannelView{ChannelId: channelId}
 | 
				
			||||||
	res, _ := m.Client.ViewChannel(m.User.Id, view)
 | 
						_, resp := m.Client.ViewChannel(m.User.Id, view)
 | 
				
			||||||
	if !res {
 | 
						if resp.Error != nil {
 | 
				
			||||||
		m.log.Errorf("ChannelView update for %s failed", channelId)
 | 
							m.log.Errorf("ChannelView update for %s failed: %s", channelId, resp.Error)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/gorilla/schema"
 | 
						"github.com/gorilla/schema"
 | 
				
			||||||
 | 
						"github.com/nlopes/slack"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
@@ -22,7 +23,7 @@ type OMessage struct {
 | 
				
			|||||||
	IconEmoji   string                 `json:"icon_emoji,omitempty"`
 | 
						IconEmoji   string                 `json:"icon_emoji,omitempty"`
 | 
				
			||||||
	UserName    string                 `json:"username,omitempty"`
 | 
						UserName    string                 `json:"username,omitempty"`
 | 
				
			||||||
	Text        string                 `json:"text"`
 | 
						Text        string                 `json:"text"`
 | 
				
			||||||
	Attachments interface{}            `json:"attachments,omitempty"`
 | 
						Attachments []slack.Attachment     `json:"attachments,omitempty"`
 | 
				
			||||||
	Type        string                 `json:"type,omitempty"`
 | 
						Type        string                 `json:"type,omitempty"`
 | 
				
			||||||
	Props       map[string]interface{} `json:"props"`
 | 
						Props       map[string]interface{} `json:"props"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,30 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
	"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var log = logrus.New()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	log.Formatter = new(logrus.TextFormatter) // default
 | 
					 | 
				
			||||||
	log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	log.WithFields(logrus.Fields{
 | 
					 | 
				
			||||||
		"animal": "walrus",
 | 
					 | 
				
			||||||
		"size":   10,
 | 
					 | 
				
			||||||
	}).Info("A group of walrus emerges from the ocean")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.WithFields(logrus.Fields{
 | 
					 | 
				
			||||||
		"omg":    true,
 | 
					 | 
				
			||||||
		"number": 122,
 | 
					 | 
				
			||||||
	}).Warn("The group's number increased tremendously!")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.WithFields(logrus.Fields{
 | 
					 | 
				
			||||||
		"omg":    true,
 | 
					 | 
				
			||||||
		"number": 100,
 | 
					 | 
				
			||||||
	}).Fatal("The ice breaks!")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										67
									
								
								vendor/github.com/Sirupsen/logrus/hooks/test/test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/Sirupsen/logrus/hooks/test/test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,67 +0,0 @@
 | 
				
			|||||||
package test
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// test.Hook is a hook designed for dealing with logs in test scenarios.
 | 
					 | 
				
			||||||
type Hook struct {
 | 
					 | 
				
			||||||
	Entries []*logrus.Entry
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Installs a test hook for the global logger.
 | 
					 | 
				
			||||||
func NewGlobal() *Hook {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	hook := new(Hook)
 | 
					 | 
				
			||||||
	logrus.AddHook(hook)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return hook
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Installs a test hook for a given local logger.
 | 
					 | 
				
			||||||
func NewLocal(logger *logrus.Logger) *Hook {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	hook := new(Hook)
 | 
					 | 
				
			||||||
	logger.Hooks.Add(hook)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return hook
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Creates a discarding logger and installs the test hook.
 | 
					 | 
				
			||||||
func NewNullLogger() (*logrus.Logger, *Hook) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger := logrus.New()
 | 
					 | 
				
			||||||
	logger.Out = ioutil.Discard
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return logger, NewLocal(logger)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (t *Hook) Fire(e *logrus.Entry) error {
 | 
					 | 
				
			||||||
	t.Entries = append(t.Entries, e)
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (t *Hook) Levels() []logrus.Level {
 | 
					 | 
				
			||||||
	return logrus.AllLevels
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LastEntry returns the last entry that was logged or nil.
 | 
					 | 
				
			||||||
func (t *Hook) LastEntry() (l *logrus.Entry) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if i := len(t.Entries) - 1; i < 0 {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return t.Entries[i]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Reset removes all Entries from this test hook.
 | 
					 | 
				
			||||||
func (t *Hook) Reset() {
 | 
					 | 
				
			||||||
	t.Entries = make([]*logrus.Entry, 0)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_appengine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_appengine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,10 +0,0 @@
 | 
				
			|||||||
// +build appengine
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package logrus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "io"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
 | 
					 | 
				
			||||||
func IsTerminal(f io.Writer) bool {
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,10 +0,0 @@
 | 
				
			|||||||
// +build darwin freebsd openbsd netbsd dragonfly
 | 
					 | 
				
			||||||
// +build !appengine
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package logrus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "syscall"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ioctlReadTermios = syscall.TIOCGETA
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Termios syscall.Termios
 | 
					 | 
				
			||||||
							
								
								
									
										28
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,28 +0,0 @@
 | 
				
			|||||||
// Based on ssh/terminal:
 | 
					 | 
				
			||||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// +build linux darwin freebsd openbsd netbsd dragonfly
 | 
					 | 
				
			||||||
// +build !appengine
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package logrus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"syscall"
 | 
					 | 
				
			||||||
	"unsafe"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
 | 
					 | 
				
			||||||
func IsTerminal(f io.Writer) bool {
 | 
					 | 
				
			||||||
	var termios Termios
 | 
					 | 
				
			||||||
	switch v := f.(type) {
 | 
					 | 
				
			||||||
	case *os.File:
 | 
					 | 
				
			||||||
		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
 | 
					 | 
				
			||||||
		return err == 0
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,21 +0,0 @@
 | 
				
			|||||||
// +build solaris,!appengine
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package logrus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"golang.org/x/sys/unix"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
					 | 
				
			||||||
func IsTerminal(f io.Writer) bool {
 | 
					 | 
				
			||||||
	switch v := f.(type) {
 | 
					 | 
				
			||||||
	case *os.File:
 | 
					 | 
				
			||||||
		_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
 | 
					 | 
				
			||||||
		return err == nil
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										33
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,33 +0,0 @@
 | 
				
			|||||||
// Based on ssh/terminal:
 | 
					 | 
				
			||||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// +build windows,!appengine
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package logrus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"syscall"
 | 
					 | 
				
			||||||
	"unsafe"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
 | 
					 | 
				
			||||||
func IsTerminal(f io.Writer) bool {
 | 
					 | 
				
			||||||
	switch v := f.(type) {
 | 
					 | 
				
			||||||
	case *os.File:
 | 
					 | 
				
			||||||
		var st uint32
 | 
					 | 
				
			||||||
		r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
 | 
					 | 
				
			||||||
		return r != 0 && e == 0
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										362
									
								
								vendor/github.com/armon/consul-api/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								vendor/github.com/armon/consul-api/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,362 @@
 | 
				
			|||||||
 | 
					Mozilla Public License, version 2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Definitions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.1. "Contributor"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means each individual or legal entity that creates, contributes to the
 | 
				
			||||||
 | 
					     creation of, or owns Covered Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.2. "Contributor Version"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means the combination of the Contributions of others (if any) used by a
 | 
				
			||||||
 | 
					     Contributor and that particular Contributor's Contribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.3. "Contribution"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means Covered Software of a particular Contributor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.4. "Covered Software"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means Source Code Form to which the initial Contributor has attached the
 | 
				
			||||||
 | 
					     notice in Exhibit A, the Executable Form of such Source Code Form, and
 | 
				
			||||||
 | 
					     Modifications of such Source Code Form, in each case including portions
 | 
				
			||||||
 | 
					     thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.5. "Incompatible With Secondary Licenses"
 | 
				
			||||||
 | 
					     means
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     a. that the initial Contributor has attached the notice described in
 | 
				
			||||||
 | 
					        Exhibit B to the Covered Software; or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     b. that the Covered Software was made available under the terms of
 | 
				
			||||||
 | 
					        version 1.1 or earlier of the License, but not also under the terms of
 | 
				
			||||||
 | 
					        a Secondary License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.6. "Executable Form"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means any form of the work other than Source Code Form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.7. "Larger Work"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means a work that combines Covered Software with other material, in a
 | 
				
			||||||
 | 
					     separate file or files, that is not Covered Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.8. "License"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.9. "Licensable"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means having the right to grant, to the maximum extent possible, whether
 | 
				
			||||||
 | 
					     at the time of the initial grant or subsequently, any and all of the
 | 
				
			||||||
 | 
					     rights conveyed by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.10. "Modifications"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     means any of the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     a. any file in Source Code Form that results from an addition to,
 | 
				
			||||||
 | 
					        deletion from, or modification of the contents of Covered Software; or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     b. any new file in Source Code Form that contains any Covered Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.11. "Patent Claims" of a Contributor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      means any patent claim(s), including without limitation, method,
 | 
				
			||||||
 | 
					      process, and apparatus claims, in any patent Licensable by such
 | 
				
			||||||
 | 
					      Contributor that would be infringed, but for the grant of the License,
 | 
				
			||||||
 | 
					      by the making, using, selling, offering for sale, having made, import,
 | 
				
			||||||
 | 
					      or transfer of either its Contributions or its Contributor Version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.12. "Secondary License"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      means either the GNU General Public License, Version 2.0, the GNU Lesser
 | 
				
			||||||
 | 
					      General Public License, Version 2.1, the GNU Affero General Public
 | 
				
			||||||
 | 
					      License, Version 3.0, or any later versions of those licenses.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.13. "Source Code Form"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      means the form of the work preferred for making modifications.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.14. "You" (or "Your")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      means an individual or a legal entity exercising rights under this
 | 
				
			||||||
 | 
					      License. For legal entities, "You" includes any entity that controls, is
 | 
				
			||||||
 | 
					      controlled by, or is under common control with You. For purposes of this
 | 
				
			||||||
 | 
					      definition, "control" means (a) the power, direct or indirect, to cause
 | 
				
			||||||
 | 
					      the direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (b) ownership of more than fifty percent (50%) of the
 | 
				
			||||||
 | 
					      outstanding shares or beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. License Grants and Conditions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.1. Grants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Each Contributor hereby grants You a world-wide, royalty-free,
 | 
				
			||||||
 | 
					     non-exclusive license:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     a. under intellectual property rights (other than patent or trademark)
 | 
				
			||||||
 | 
					        Licensable by such Contributor to use, reproduce, make available,
 | 
				
			||||||
 | 
					        modify, display, perform, distribute, and otherwise exploit its
 | 
				
			||||||
 | 
					        Contributions, either on an unmodified basis, with Modifications, or
 | 
				
			||||||
 | 
					        as part of a Larger Work; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     b. under Patent Claims of such Contributor to make, use, sell, offer for
 | 
				
			||||||
 | 
					        sale, have made, import, and otherwise transfer either its
 | 
				
			||||||
 | 
					        Contributions or its Contributor Version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.2. Effective Date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     The licenses granted in Section 2.1 with respect to any Contribution
 | 
				
			||||||
 | 
					     become effective for each Contribution on the date the Contributor first
 | 
				
			||||||
 | 
					     distributes such Contribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.3. Limitations on Grant Scope
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     The licenses granted in this Section 2 are the only rights granted under
 | 
				
			||||||
 | 
					     this License. No additional rights or licenses will be implied from the
 | 
				
			||||||
 | 
					     distribution or licensing of Covered Software under this License.
 | 
				
			||||||
 | 
					     Notwithstanding Section 2.1(b) above, no patent license is granted by a
 | 
				
			||||||
 | 
					     Contributor:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     a. for any code that a Contributor has removed from Covered Software; or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     b. for infringements caused by: (i) Your and any other third party's
 | 
				
			||||||
 | 
					        modifications of Covered Software, or (ii) the combination of its
 | 
				
			||||||
 | 
					        Contributions with other software (except as part of its Contributor
 | 
				
			||||||
 | 
					        Version); or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     c. under Patent Claims infringed by Covered Software in the absence of
 | 
				
			||||||
 | 
					        its Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     This License does not grant any rights in the trademarks, service marks,
 | 
				
			||||||
 | 
					     or logos of any Contributor (except as may be necessary to comply with
 | 
				
			||||||
 | 
					     the notice requirements in Section 3.4).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.4. Subsequent Licenses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     No Contributor makes additional grants as a result of Your choice to
 | 
				
			||||||
 | 
					     distribute the Covered Software under a subsequent version of this
 | 
				
			||||||
 | 
					     License (see Section 10.2) or under the terms of a Secondary License (if
 | 
				
			||||||
 | 
					     permitted under the terms of Section 3.3).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.5. Representation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Each Contributor represents that the Contributor believes its
 | 
				
			||||||
 | 
					     Contributions are its original creation(s) or it has sufficient rights to
 | 
				
			||||||
 | 
					     grant the rights to its Contributions conveyed by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.6. Fair Use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     This License is not intended to limit any rights You have under
 | 
				
			||||||
 | 
					     applicable copyright doctrines of fair use, fair dealing, or other
 | 
				
			||||||
 | 
					     equivalents.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.7. Conditions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
 | 
				
			||||||
 | 
					     Section 2.1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. Responsibilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.1. Distribution of Source Form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     All distribution of Covered Software in Source Code Form, including any
 | 
				
			||||||
 | 
					     Modifications that You create or to which You contribute, must be under
 | 
				
			||||||
 | 
					     the terms of this License. You must inform recipients that the Source
 | 
				
			||||||
 | 
					     Code Form of the Covered Software is governed by the terms of this
 | 
				
			||||||
 | 
					     License, and how they can obtain a copy of this License. You may not
 | 
				
			||||||
 | 
					     attempt to alter or restrict the recipients' rights in the Source Code
 | 
				
			||||||
 | 
					     Form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.2. Distribution of Executable Form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     If You distribute Covered Software in Executable Form then:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     a. such Covered Software must also be made available in Source Code Form,
 | 
				
			||||||
 | 
					        as described in Section 3.1, and You must inform recipients of the
 | 
				
			||||||
 | 
					        Executable Form how they can obtain a copy of such Source Code Form by
 | 
				
			||||||
 | 
					        reasonable means in a timely manner, at a charge no more than the cost
 | 
				
			||||||
 | 
					        of distribution to the recipient; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     b. You may distribute such Executable Form under the terms of this
 | 
				
			||||||
 | 
					        License, or sublicense it under different terms, provided that the
 | 
				
			||||||
 | 
					        license for the Executable Form does not attempt to limit or alter the
 | 
				
			||||||
 | 
					        recipients' rights in the Source Code Form under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.3. Distribution of a Larger Work
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     You may create and distribute a Larger Work under terms of Your choice,
 | 
				
			||||||
 | 
					     provided that You also comply with the requirements of this License for
 | 
				
			||||||
 | 
					     the Covered Software. If the Larger Work is a combination of Covered
 | 
				
			||||||
 | 
					     Software with a work governed by one or more Secondary Licenses, and the
 | 
				
			||||||
 | 
					     Covered Software is not Incompatible With Secondary Licenses, this
 | 
				
			||||||
 | 
					     License permits You to additionally distribute such Covered Software
 | 
				
			||||||
 | 
					     under the terms of such Secondary License(s), so that the recipient of
 | 
				
			||||||
 | 
					     the Larger Work may, at their option, further distribute the Covered
 | 
				
			||||||
 | 
					     Software under the terms of either this License or such Secondary
 | 
				
			||||||
 | 
					     License(s).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.4. Notices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     You may not remove or alter the substance of any license notices
 | 
				
			||||||
 | 
					     (including copyright notices, patent notices, disclaimers of warranty, or
 | 
				
			||||||
 | 
					     limitations of liability) contained within the Source Code Form of the
 | 
				
			||||||
 | 
					     Covered Software, except that You may alter any license notices to the
 | 
				
			||||||
 | 
					     extent required to remedy known factual inaccuracies.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.5. Application of Additional Terms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     You may choose to offer, and to charge a fee for, warranty, support,
 | 
				
			||||||
 | 
					     indemnity or liability obligations to one or more recipients of Covered
 | 
				
			||||||
 | 
					     Software. However, You may do so only on Your own behalf, and not on
 | 
				
			||||||
 | 
					     behalf of any Contributor. You must make it absolutely clear that any
 | 
				
			||||||
 | 
					     such warranty, support, indemnity, or liability obligation is offered by
 | 
				
			||||||
 | 
					     You alone, and You hereby agree to indemnify every Contributor for any
 | 
				
			||||||
 | 
					     liability incurred by such Contributor as a result of warranty, support,
 | 
				
			||||||
 | 
					     indemnity or liability terms You offer. You may include additional
 | 
				
			||||||
 | 
					     disclaimers of warranty and limitations of liability specific to any
 | 
				
			||||||
 | 
					     jurisdiction.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. Inability to Comply Due to Statute or Regulation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   If it is impossible for You to comply with any of the terms of this License
 | 
				
			||||||
 | 
					   with respect to some or all of the Covered Software due to statute,
 | 
				
			||||||
 | 
					   judicial order, or regulation then You must: (a) comply with the terms of
 | 
				
			||||||
 | 
					   this License to the maximum extent possible; and (b) describe the
 | 
				
			||||||
 | 
					   limitations and the code they affect. Such description must be placed in a
 | 
				
			||||||
 | 
					   text file included with all distributions of the Covered Software under
 | 
				
			||||||
 | 
					   this License. Except to the extent prohibited by statute or regulation,
 | 
				
			||||||
 | 
					   such description must be sufficiently detailed for a recipient of ordinary
 | 
				
			||||||
 | 
					   skill to be able to understand it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5. Termination
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5.1. The rights granted under this License will terminate automatically if You
 | 
				
			||||||
 | 
					     fail to comply with any of its terms. However, if You become compliant,
 | 
				
			||||||
 | 
					     then the rights granted under this License from a particular Contributor
 | 
				
			||||||
 | 
					     are reinstated (a) provisionally, unless and until such Contributor
 | 
				
			||||||
 | 
					     explicitly and finally terminates Your grants, and (b) on an ongoing
 | 
				
			||||||
 | 
					     basis, if such Contributor fails to notify You of the non-compliance by
 | 
				
			||||||
 | 
					     some reasonable means prior to 60 days after You have come back into
 | 
				
			||||||
 | 
					     compliance. Moreover, Your grants from a particular Contributor are
 | 
				
			||||||
 | 
					     reinstated on an ongoing basis if such Contributor notifies You of the
 | 
				
			||||||
 | 
					     non-compliance by some reasonable means, this is the first time You have
 | 
				
			||||||
 | 
					     received notice of non-compliance with this License from such
 | 
				
			||||||
 | 
					     Contributor, and You become compliant prior to 30 days after Your receipt
 | 
				
			||||||
 | 
					     of the notice.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5.2. If You initiate litigation against any entity by asserting a patent
 | 
				
			||||||
 | 
					     infringement claim (excluding declaratory judgment actions,
 | 
				
			||||||
 | 
					     counter-claims, and cross-claims) alleging that a Contributor Version
 | 
				
			||||||
 | 
					     directly or indirectly infringes any patent, then the rights granted to
 | 
				
			||||||
 | 
					     You by any and all Contributors for the Covered Software under Section
 | 
				
			||||||
 | 
					     2.1 of this License shall terminate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
 | 
				
			||||||
 | 
					     license agreements (excluding distributors and resellers) which have been
 | 
				
			||||||
 | 
					     validly granted by You or Your distributors under this License prior to
 | 
				
			||||||
 | 
					     termination shall survive termination.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6. Disclaimer of Warranty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Covered Software is provided under this License on an "as is" basis,
 | 
				
			||||||
 | 
					   without warranty of any kind, either expressed, implied, or statutory,
 | 
				
			||||||
 | 
					   including, without limitation, warranties that the Covered Software is free
 | 
				
			||||||
 | 
					   of defects, merchantable, fit for a particular purpose or non-infringing.
 | 
				
			||||||
 | 
					   The entire risk as to the quality and performance of the Covered Software
 | 
				
			||||||
 | 
					   is with You. Should any Covered Software prove defective in any respect,
 | 
				
			||||||
 | 
					   You (not any Contributor) assume the cost of any necessary servicing,
 | 
				
			||||||
 | 
					   repair, or correction. This disclaimer of warranty constitutes an essential
 | 
				
			||||||
 | 
					   part of this License. No use of  any Covered Software is authorized under
 | 
				
			||||||
 | 
					   this License except under this disclaimer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					7. Limitation of Liability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Under no circumstances and under no legal theory, whether tort (including
 | 
				
			||||||
 | 
					   negligence), contract, or otherwise, shall any Contributor, or anyone who
 | 
				
			||||||
 | 
					   distributes Covered Software as permitted above, be liable to You for any
 | 
				
			||||||
 | 
					   direct, indirect, special, incidental, or consequential damages of any
 | 
				
			||||||
 | 
					   character including, without limitation, damages for lost profits, loss of
 | 
				
			||||||
 | 
					   goodwill, work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					   other commercial damages or losses, even if such party shall have been
 | 
				
			||||||
 | 
					   informed of the possibility of such damages. This limitation of liability
 | 
				
			||||||
 | 
					   shall not apply to liability for death or personal injury resulting from
 | 
				
			||||||
 | 
					   such party's negligence to the extent applicable law prohibits such
 | 
				
			||||||
 | 
					   limitation. Some jurisdictions do not allow the exclusion or limitation of
 | 
				
			||||||
 | 
					   incidental or consequential damages, so this exclusion and limitation may
 | 
				
			||||||
 | 
					   not apply to You.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					8. Litigation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Any litigation relating to this License may be brought only in the courts
 | 
				
			||||||
 | 
					   of a jurisdiction where the defendant maintains its principal place of
 | 
				
			||||||
 | 
					   business and such litigation shall be governed by laws of that
 | 
				
			||||||
 | 
					   jurisdiction, without reference to its conflict-of-law provisions. Nothing
 | 
				
			||||||
 | 
					   in this Section shall prevent a party's ability to bring cross-claims or
 | 
				
			||||||
 | 
					   counter-claims.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					9. Miscellaneous
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   This License represents the complete agreement concerning the subject
 | 
				
			||||||
 | 
					   matter hereof. If any provision of this License is held to be
 | 
				
			||||||
 | 
					   unenforceable, such provision shall be reformed only to the extent
 | 
				
			||||||
 | 
					   necessary to make it enforceable. Any law or regulation which provides that
 | 
				
			||||||
 | 
					   the language of a contract shall be construed against the drafter shall not
 | 
				
			||||||
 | 
					   be used to construe this License against a Contributor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					10. Versions of the License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					10.1. New Versions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Mozilla Foundation is the license steward. Except as provided in Section
 | 
				
			||||||
 | 
					      10.3, no one other than the license steward has the right to modify or
 | 
				
			||||||
 | 
					      publish new versions of this License. Each version will be given a
 | 
				
			||||||
 | 
					      distinguishing version number.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					10.2. Effect of New Versions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may distribute the Covered Software under the terms of the version
 | 
				
			||||||
 | 
					      of the License under which You originally received the Covered Software,
 | 
				
			||||||
 | 
					      or under the terms of any subsequent version published by the license
 | 
				
			||||||
 | 
					      steward.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					10.3. Modified Versions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      If you create software not governed by this License, and you want to
 | 
				
			||||||
 | 
					      create a new license for such software, you may create and use a
 | 
				
			||||||
 | 
					      modified version of this License if you rename the license and remove
 | 
				
			||||||
 | 
					      any references to the name of the license steward (except to note that
 | 
				
			||||||
 | 
					      such modified license differs from this License).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					10.4. Distributing Source Code Form that is Incompatible With Secondary
 | 
				
			||||||
 | 
					      Licenses If You choose to distribute Source Code Form that is
 | 
				
			||||||
 | 
					      Incompatible With Secondary Licenses under the terms of this version of
 | 
				
			||||||
 | 
					      the License, the notice described in Exhibit B of this License must be
 | 
				
			||||||
 | 
					      attached.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Exhibit A - Source Code Form License Notice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      This Source Code Form is subject to the
 | 
				
			||||||
 | 
					      terms of the Mozilla Public License, v.
 | 
				
			||||||
 | 
					      2.0. If a copy of the MPL was not
 | 
				
			||||||
 | 
					      distributed with this file, You can
 | 
				
			||||||
 | 
					      obtain one at
 | 
				
			||||||
 | 
					      http://mozilla.org/MPL/2.0/.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If it is not possible or desirable to put the notice in a particular file,
 | 
				
			||||||
 | 
					then You may include the notice in a location (such as a LICENSE file in a
 | 
				
			||||||
 | 
					relevant directory) where a recipient would be likely to look for such a
 | 
				
			||||||
 | 
					notice.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You may add additional accurate notices of copyright ownership.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Exhibit B - "Incompatible With Secondary Licenses" Notice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      This Source Code Form is "Incompatible
 | 
				
			||||||
 | 
					      With Secondary Licenses", as defined by
 | 
				
			||||||
 | 
					      the Mozilla Public License, v. 2.0.
 | 
				
			||||||
							
								
								
									
										140
									
								
								vendor/github.com/armon/consul-api/acl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								vendor/github.com/armon/consul-api/acl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// ACLCLientType is the client type token
 | 
				
			||||||
 | 
						ACLClientType = "client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ACLManagementType is the management type token
 | 
				
			||||||
 | 
						ACLManagementType = "management"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ACLEntry is used to represent an ACL entry
 | 
				
			||||||
 | 
					type ACLEntry struct {
 | 
				
			||||||
 | 
						CreateIndex uint64
 | 
				
			||||||
 | 
						ModifyIndex uint64
 | 
				
			||||||
 | 
						ID          string
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Type        string
 | 
				
			||||||
 | 
						Rules       string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ACL can be used to query the ACL endpoints
 | 
				
			||||||
 | 
					type ACL struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ACL returns a handle to the ACL endpoints
 | 
				
			||||||
 | 
					func (c *Client) ACL() *ACL {
 | 
				
			||||||
 | 
						return &ACL{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create is used to generate a new token with the given parameters
 | 
				
			||||||
 | 
					func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/acl/create")
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						r.obj = acl
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						var out struct{ ID string }
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out.ID, wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update is used to update the rules of an existing token
 | 
				
			||||||
 | 
					func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/acl/update")
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						r.obj = acl
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						return wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Destroy is used to destroy a given ACL token ID
 | 
				
			||||||
 | 
					func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						return wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clone is used to return a new token cloned from an existing one
 | 
				
			||||||
 | 
					func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						var out struct{ ID string }
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out.ID, wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Info is used to query for information about an ACL token
 | 
				
			||||||
 | 
					func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("GET", "/v1/acl/info/"+id)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*ACLEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(entries) > 0 {
 | 
				
			||||||
 | 
							return entries[0], qm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List is used to get all the ACL tokens
 | 
				
			||||||
 | 
					func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("GET", "/v1/acl/list")
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*ACLEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										272
									
								
								vendor/github.com/armon/consul-api/agent.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								vendor/github.com/armon/consul-api/agent.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AgentCheck represents a check known to the agent
 | 
				
			||||||
 | 
					type AgentCheck struct {
 | 
				
			||||||
 | 
						Node        string
 | 
				
			||||||
 | 
						CheckID     string
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Status      string
 | 
				
			||||||
 | 
						Notes       string
 | 
				
			||||||
 | 
						Output      string
 | 
				
			||||||
 | 
						ServiceID   string
 | 
				
			||||||
 | 
						ServiceName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AgentService represents a service known to the agent
 | 
				
			||||||
 | 
					type AgentService struct {
 | 
				
			||||||
 | 
						ID      string
 | 
				
			||||||
 | 
						Service string
 | 
				
			||||||
 | 
						Tags    []string
 | 
				
			||||||
 | 
						Port    int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AgentMember represents a cluster member known to the agent
 | 
				
			||||||
 | 
					type AgentMember struct {
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Addr        string
 | 
				
			||||||
 | 
						Port        uint16
 | 
				
			||||||
 | 
						Tags        map[string]string
 | 
				
			||||||
 | 
						Status      int
 | 
				
			||||||
 | 
						ProtocolMin uint8
 | 
				
			||||||
 | 
						ProtocolMax uint8
 | 
				
			||||||
 | 
						ProtocolCur uint8
 | 
				
			||||||
 | 
						DelegateMin uint8
 | 
				
			||||||
 | 
						DelegateMax uint8
 | 
				
			||||||
 | 
						DelegateCur uint8
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AgentServiceRegistration is used to register a new service
 | 
				
			||||||
 | 
					type AgentServiceRegistration struct {
 | 
				
			||||||
 | 
						ID    string   `json:",omitempty"`
 | 
				
			||||||
 | 
						Name  string   `json:",omitempty"`
 | 
				
			||||||
 | 
						Tags  []string `json:",omitempty"`
 | 
				
			||||||
 | 
						Port  int      `json:",omitempty"`
 | 
				
			||||||
 | 
						Check *AgentServiceCheck
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AgentCheckRegistration is used to register a new check
 | 
				
			||||||
 | 
					type AgentCheckRegistration struct {
 | 
				
			||||||
 | 
						ID    string `json:",omitempty"`
 | 
				
			||||||
 | 
						Name  string `json:",omitempty"`
 | 
				
			||||||
 | 
						Notes string `json:",omitempty"`
 | 
				
			||||||
 | 
						AgentServiceCheck
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AgentServiceCheck is used to create an associated
 | 
				
			||||||
 | 
					// check for a service
 | 
				
			||||||
 | 
					type AgentServiceCheck struct {
 | 
				
			||||||
 | 
						Script   string `json:",omitempty"`
 | 
				
			||||||
 | 
						Interval string `json:",omitempty"`
 | 
				
			||||||
 | 
						TTL      string `json:",omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Agent can be used to query the Agent endpoints
 | 
				
			||||||
 | 
					type Agent struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// cache the node name
 | 
				
			||||||
 | 
						nodeName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Agent returns a handle to the agent endpoints
 | 
				
			||||||
 | 
					func (c *Client) Agent() *Agent {
 | 
				
			||||||
 | 
						return &Agent{c: c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Self is used to query the agent we are speaking to for
 | 
				
			||||||
 | 
					// information about itself
 | 
				
			||||||
 | 
					func (a *Agent) Self() (map[string]map[string]interface{}, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("GET", "/v1/agent/self")
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out map[string]map[string]interface{}
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NodeName is used to get the node name of the agent
 | 
				
			||||||
 | 
					func (a *Agent) NodeName() (string, error) {
 | 
				
			||||||
 | 
						if a.nodeName != "" {
 | 
				
			||||||
 | 
							return a.nodeName, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						info, err := a.Self()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name := info["Config"]["NodeName"].(string)
 | 
				
			||||||
 | 
						a.nodeName = name
 | 
				
			||||||
 | 
						return name, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Checks returns the locally registered checks
 | 
				
			||||||
 | 
					func (a *Agent) Checks() (map[string]*AgentCheck, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("GET", "/v1/agent/checks")
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out map[string]*AgentCheck
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Services returns the locally registered services
 | 
				
			||||||
 | 
					func (a *Agent) Services() (map[string]*AgentService, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("GET", "/v1/agent/services")
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out map[string]*AgentService
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Members returns the known gossip members. The WAN
 | 
				
			||||||
 | 
					// flag can be used to query a server for WAN members.
 | 
				
			||||||
 | 
					func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
 | 
				
			||||||
 | 
						r := a.c.newRequest("GET", "/v1/agent/members")
 | 
				
			||||||
 | 
						if wan {
 | 
				
			||||||
 | 
							r.params.Set("wan", "1")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*AgentMember
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ServiceRegister is used to register a new service with
 | 
				
			||||||
 | 
					// the local agent
 | 
				
			||||||
 | 
					func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/agent/service/register")
 | 
				
			||||||
 | 
						r.obj = service
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ServiceDeregister is used to deregister a service with
 | 
				
			||||||
 | 
					// the local agent
 | 
				
			||||||
 | 
					func (a *Agent) ServiceDeregister(serviceID string) error {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PassTTL is used to set a TTL check to the passing state
 | 
				
			||||||
 | 
					func (a *Agent) PassTTL(checkID, note string) error {
 | 
				
			||||||
 | 
						return a.UpdateTTL(checkID, note, "pass")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WarnTTL is used to set a TTL check to the warning state
 | 
				
			||||||
 | 
					func (a *Agent) WarnTTL(checkID, note string) error {
 | 
				
			||||||
 | 
						return a.UpdateTTL(checkID, note, "warn")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FailTTL is used to set a TTL check to the failing state
 | 
				
			||||||
 | 
					func (a *Agent) FailTTL(checkID, note string) error {
 | 
				
			||||||
 | 
						return a.UpdateTTL(checkID, note, "fail")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateTTL is used to update the TTL of a check
 | 
				
			||||||
 | 
					func (a *Agent) UpdateTTL(checkID, note, status string) error {
 | 
				
			||||||
 | 
						switch status {
 | 
				
			||||||
 | 
						case "pass":
 | 
				
			||||||
 | 
						case "warn":
 | 
				
			||||||
 | 
						case "fail":
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("Invalid status: %s", status)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", endpoint)
 | 
				
			||||||
 | 
						r.params.Set("note", note)
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CheckRegister is used to register a new check with
 | 
				
			||||||
 | 
					// the local agent
 | 
				
			||||||
 | 
					func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/agent/check/register")
 | 
				
			||||||
 | 
						r.obj = check
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CheckDeregister is used to deregister a check with
 | 
				
			||||||
 | 
					// the local agent
 | 
				
			||||||
 | 
					func (a *Agent) CheckDeregister(checkID string) error {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Join is used to instruct the agent to attempt a join to
 | 
				
			||||||
 | 
					// another cluster member
 | 
				
			||||||
 | 
					func (a *Agent) Join(addr string, wan bool) error {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
 | 
				
			||||||
 | 
						if wan {
 | 
				
			||||||
 | 
							r.params.Set("wan", "1")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ForceLeave is used to have the agent eject a failed node
 | 
				
			||||||
 | 
					func (a *Agent) ForceLeave(node string) error {
 | 
				
			||||||
 | 
						r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
 | 
				
			||||||
 | 
						_, resp, err := requireOK(a.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										323
									
								
								vendor/github.com/armon/consul-api/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								vendor/github.com/armon/consul-api/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,323 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// QueryOptions are used to parameterize a query
 | 
				
			||||||
 | 
					type QueryOptions struct {
 | 
				
			||||||
 | 
						// Providing a datacenter overwrites the DC provided
 | 
				
			||||||
 | 
						// by the Config
 | 
				
			||||||
 | 
						Datacenter string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// AllowStale allows any Consul server (non-leader) to service
 | 
				
			||||||
 | 
						// a read. This allows for lower latency and higher throughput
 | 
				
			||||||
 | 
						AllowStale bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RequireConsistent forces the read to be fully consistent.
 | 
				
			||||||
 | 
						// This is more expensive but prevents ever performing a stale
 | 
				
			||||||
 | 
						// read.
 | 
				
			||||||
 | 
						RequireConsistent bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// WaitIndex is used to enable a blocking query. Waits
 | 
				
			||||||
 | 
						// until the timeout or the next index is reached
 | 
				
			||||||
 | 
						WaitIndex uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// WaitTime is used to bound the duration of a wait.
 | 
				
			||||||
 | 
						// Defaults to that of the Config, but can be overriden.
 | 
				
			||||||
 | 
						WaitTime time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Token is used to provide a per-request ACL token
 | 
				
			||||||
 | 
						// which overrides the agent's default token.
 | 
				
			||||||
 | 
						Token string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WriteOptions are used to parameterize a write
 | 
				
			||||||
 | 
					type WriteOptions struct {
 | 
				
			||||||
 | 
						// Providing a datacenter overwrites the DC provided
 | 
				
			||||||
 | 
						// by the Config
 | 
				
			||||||
 | 
						Datacenter string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Token is used to provide a per-request ACL token
 | 
				
			||||||
 | 
						// which overrides the agent's default token.
 | 
				
			||||||
 | 
						Token string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// QueryMeta is used to return meta data about a query
 | 
				
			||||||
 | 
					type QueryMeta struct {
 | 
				
			||||||
 | 
						// LastIndex. This can be used as a WaitIndex to perform
 | 
				
			||||||
 | 
						// a blocking query
 | 
				
			||||||
 | 
						LastIndex uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Time of last contact from the leader for the
 | 
				
			||||||
 | 
						// server servicing the request
 | 
				
			||||||
 | 
						LastContact time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Is there a known leader
 | 
				
			||||||
 | 
						KnownLeader bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// How long did the request take
 | 
				
			||||||
 | 
						RequestTime time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WriteMeta is used to return meta data about a write
 | 
				
			||||||
 | 
					type WriteMeta struct {
 | 
				
			||||||
 | 
						// How long did the request take
 | 
				
			||||||
 | 
						RequestTime time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HttpBasicAuth is used to authenticate http client with HTTP Basic Authentication
 | 
				
			||||||
 | 
					type HttpBasicAuth struct {
 | 
				
			||||||
 | 
						// Username to use for HTTP Basic Authentication
 | 
				
			||||||
 | 
						Username string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Password to use for HTTP Basic Authentication
 | 
				
			||||||
 | 
						Password string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Config is used to configure the creation of a client
 | 
				
			||||||
 | 
					type Config struct {
 | 
				
			||||||
 | 
						// Address is the address of the Consul server
 | 
				
			||||||
 | 
						Address string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Scheme is the URI scheme for the Consul server
 | 
				
			||||||
 | 
						Scheme string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Datacenter to use. If not provided, the default agent datacenter is used.
 | 
				
			||||||
 | 
						Datacenter string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// HttpClient is the client to use. Default will be
 | 
				
			||||||
 | 
						// used if not provided.
 | 
				
			||||||
 | 
						HttpClient *http.Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// HttpAuth is the auth info to use for http access.
 | 
				
			||||||
 | 
						HttpAuth *HttpBasicAuth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// WaitTime limits how long a Watch will block. If not provided,
 | 
				
			||||||
 | 
						// the agent default values will be used.
 | 
				
			||||||
 | 
						WaitTime time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Token is used to provide a per-request ACL token
 | 
				
			||||||
 | 
						// which overrides the agent's default token.
 | 
				
			||||||
 | 
						Token string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DefaultConfig returns a default configuration for the client
 | 
				
			||||||
 | 
					func DefaultConfig() *Config {
 | 
				
			||||||
 | 
						return &Config{
 | 
				
			||||||
 | 
							Address:    "127.0.0.1:8500",
 | 
				
			||||||
 | 
							Scheme:     "http",
 | 
				
			||||||
 | 
							HttpClient: http.DefaultClient,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Client provides a client to the Consul API
 | 
				
			||||||
 | 
					type Client struct {
 | 
				
			||||||
 | 
						config Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewClient returns a new client
 | 
				
			||||||
 | 
					func NewClient(config *Config) (*Client, error) {
 | 
				
			||||||
 | 
						// bootstrap the config
 | 
				
			||||||
 | 
						defConfig := DefaultConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(config.Address) == 0 {
 | 
				
			||||||
 | 
							config.Address = defConfig.Address
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(config.Scheme) == 0 {
 | 
				
			||||||
 | 
							config.Scheme = defConfig.Scheme
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.HttpClient == nil {
 | 
				
			||||||
 | 
							config.HttpClient = defConfig.HttpClient
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := &Client{
 | 
				
			||||||
 | 
							config: *config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return client, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// request is used to help build up a request
 | 
				
			||||||
 | 
					type request struct {
 | 
				
			||||||
 | 
						config *Config
 | 
				
			||||||
 | 
						method string
 | 
				
			||||||
 | 
						url    *url.URL
 | 
				
			||||||
 | 
						params url.Values
 | 
				
			||||||
 | 
						body   io.Reader
 | 
				
			||||||
 | 
						obj    interface{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setQueryOptions is used to annotate the request with
 | 
				
			||||||
 | 
					// additional query options
 | 
				
			||||||
 | 
					func (r *request) setQueryOptions(q *QueryOptions) {
 | 
				
			||||||
 | 
						if q == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.Datacenter != "" {
 | 
				
			||||||
 | 
							r.params.Set("dc", q.Datacenter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.AllowStale {
 | 
				
			||||||
 | 
							r.params.Set("stale", "")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.RequireConsistent {
 | 
				
			||||||
 | 
							r.params.Set("consistent", "")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.WaitIndex != 0 {
 | 
				
			||||||
 | 
							r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.WaitTime != 0 {
 | 
				
			||||||
 | 
							r.params.Set("wait", durToMsec(q.WaitTime))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.Token != "" {
 | 
				
			||||||
 | 
							r.params.Set("token", q.Token)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// durToMsec converts a duration to a millisecond specified string
 | 
				
			||||||
 | 
					func durToMsec(dur time.Duration) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%dms", dur/time.Millisecond)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setWriteOptions is used to annotate the request with
 | 
				
			||||||
 | 
					// additional write options
 | 
				
			||||||
 | 
					func (r *request) setWriteOptions(q *WriteOptions) {
 | 
				
			||||||
 | 
						if q == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.Datacenter != "" {
 | 
				
			||||||
 | 
							r.params.Set("dc", q.Datacenter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if q.Token != "" {
 | 
				
			||||||
 | 
							r.params.Set("token", q.Token)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// toHTTP converts the request to an HTTP request
 | 
				
			||||||
 | 
					func (r *request) toHTTP() (*http.Request, error) {
 | 
				
			||||||
 | 
						// Encode the query parameters
 | 
				
			||||||
 | 
						r.url.RawQuery = r.params.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get the url sring
 | 
				
			||||||
 | 
						urlRaw := r.url.String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if we should encode the body
 | 
				
			||||||
 | 
						if r.body == nil && r.obj != nil {
 | 
				
			||||||
 | 
							if b, err := encodeBody(r.obj); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								r.body = b
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create the HTTP request
 | 
				
			||||||
 | 
						req, err := http.NewRequest(r.method, urlRaw, r.body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Setup auth
 | 
				
			||||||
 | 
						if err == nil && r.config.HttpAuth != nil {
 | 
				
			||||||
 | 
							req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return req, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// newRequest is used to create a new request
 | 
				
			||||||
 | 
					func (c *Client) newRequest(method, path string) *request {
 | 
				
			||||||
 | 
						r := &request{
 | 
				
			||||||
 | 
							config: &c.config,
 | 
				
			||||||
 | 
							method: method,
 | 
				
			||||||
 | 
							url: &url.URL{
 | 
				
			||||||
 | 
								Scheme: c.config.Scheme,
 | 
				
			||||||
 | 
								Host:   c.config.Address,
 | 
				
			||||||
 | 
								Path:   path,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							params: make(map[string][]string),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.config.Datacenter != "" {
 | 
				
			||||||
 | 
							r.params.Set("dc", c.config.Datacenter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.config.WaitTime != 0 {
 | 
				
			||||||
 | 
							r.params.Set("wait", durToMsec(r.config.WaitTime))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.config.Token != "" {
 | 
				
			||||||
 | 
							r.params.Set("token", r.config.Token)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// doRequest runs a request with our client
 | 
				
			||||||
 | 
					func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
 | 
				
			||||||
 | 
						req, err := r.toHTTP()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						start := time.Now()
 | 
				
			||||||
 | 
						resp, err := c.config.HttpClient.Do(req)
 | 
				
			||||||
 | 
						diff := time.Now().Sub(start)
 | 
				
			||||||
 | 
						return diff, resp, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// parseQueryMeta is used to help parse query meta-data
 | 
				
			||||||
 | 
					func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
 | 
				
			||||||
 | 
						header := resp.Header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Parse the X-Consul-Index
 | 
				
			||||||
 | 
						index, err := strconv.ParseUint(header.Get("X-Consul-Index"), 10, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Failed to parse X-Consul-Index: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						q.LastIndex = index
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Parse the X-Consul-LastContact
 | 
				
			||||||
 | 
						last, err := strconv.ParseUint(header.Get("X-Consul-LastContact"), 10, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Failed to parse X-Consul-LastContact: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						q.LastContact = time.Duration(last) * time.Millisecond
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Parse the X-Consul-KnownLeader
 | 
				
			||||||
 | 
						switch header.Get("X-Consul-KnownLeader") {
 | 
				
			||||||
 | 
						case "true":
 | 
				
			||||||
 | 
							q.KnownLeader = true
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							q.KnownLeader = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// decodeBody is used to JSON decode a body
 | 
				
			||||||
 | 
					func decodeBody(resp *http.Response, out interface{}) error {
 | 
				
			||||||
 | 
						dec := json.NewDecoder(resp.Body)
 | 
				
			||||||
 | 
						return dec.Decode(out)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// encodeBody is used to encode a request body
 | 
				
			||||||
 | 
					func encodeBody(obj interface{}) (io.Reader, error) {
 | 
				
			||||||
 | 
						buf := bytes.NewBuffer(nil)
 | 
				
			||||||
 | 
						enc := json.NewEncoder(buf)
 | 
				
			||||||
 | 
						if err := enc.Encode(obj); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return buf, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// requireOK is used to wrap doRequest and check for a 200
 | 
				
			||||||
 | 
					func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
 | 
				
			||||||
 | 
						if e != nil {
 | 
				
			||||||
 | 
							return d, resp, e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp.StatusCode != 200 {
 | 
				
			||||||
 | 
							var buf bytes.Buffer
 | 
				
			||||||
 | 
							io.Copy(&buf, resp.Body)
 | 
				
			||||||
 | 
							return d, resp, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d, resp, e
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										181
									
								
								vendor/github.com/armon/consul-api/catalog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								vendor/github.com/armon/consul-api/catalog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Node struct {
 | 
				
			||||||
 | 
						Node    string
 | 
				
			||||||
 | 
						Address string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CatalogService struct {
 | 
				
			||||||
 | 
						Node        string
 | 
				
			||||||
 | 
						Address     string
 | 
				
			||||||
 | 
						ServiceID   string
 | 
				
			||||||
 | 
						ServiceName string
 | 
				
			||||||
 | 
						ServiceTags []string
 | 
				
			||||||
 | 
						ServicePort int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CatalogNode struct {
 | 
				
			||||||
 | 
						Node     *Node
 | 
				
			||||||
 | 
						Services map[string]*AgentService
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CatalogRegistration struct {
 | 
				
			||||||
 | 
						Node       string
 | 
				
			||||||
 | 
						Address    string
 | 
				
			||||||
 | 
						Datacenter string
 | 
				
			||||||
 | 
						Service    *AgentService
 | 
				
			||||||
 | 
						Check      *AgentCheck
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CatalogDeregistration struct {
 | 
				
			||||||
 | 
						Node       string
 | 
				
			||||||
 | 
						Address    string
 | 
				
			||||||
 | 
						Datacenter string
 | 
				
			||||||
 | 
						ServiceID  string
 | 
				
			||||||
 | 
						CheckID    string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Catalog can be used to query the Catalog endpoints
 | 
				
			||||||
 | 
					type Catalog struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Catalog returns a handle to the catalog endpoints
 | 
				
			||||||
 | 
					func (c *Client) Catalog() *Catalog {
 | 
				
			||||||
 | 
						return &Catalog{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Catalog) Register(reg *CatalogRegistration, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("PUT", "/v1/catalog/register")
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						r.obj = reg
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{}
 | 
				
			||||||
 | 
						wm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Catalog) Deregister(dereg *CatalogDeregistration, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("PUT", "/v1/catalog/deregister")
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						r.obj = dereg
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{}
 | 
				
			||||||
 | 
						wm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Datacenters is used to query for all the known datacenters
 | 
				
			||||||
 | 
					func (c *Catalog) Datacenters() ([]string, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("GET", "/v1/catalog/datacenters")
 | 
				
			||||||
 | 
						_, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []string
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Nodes is used to query all the known nodes
 | 
				
			||||||
 | 
					func (c *Catalog) Nodes(q *QueryOptions) ([]*Node, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("GET", "/v1/catalog/nodes")
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*Node
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Services is used to query for all known services
 | 
				
			||||||
 | 
					func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("GET", "/v1/catalog/services")
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out map[string][]string
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Service is used to query catalog entries for a given service
 | 
				
			||||||
 | 
					func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("GET", "/v1/catalog/service/"+service)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						if tag != "" {
 | 
				
			||||||
 | 
							r.params.Set("tag", tag)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*CatalogService
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Node is used to query for service information about a single node
 | 
				
			||||||
 | 
					func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := c.c.newRequest("GET", "/v1/catalog/node/"+node)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(c.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out *CatalogNode
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								vendor/github.com/armon/consul-api/event.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								vendor/github.com/armon/consul-api/event.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Event can be used to query the Event endpoints
 | 
				
			||||||
 | 
					type Event struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UserEvent represents an event that was fired by the user
 | 
				
			||||||
 | 
					type UserEvent struct {
 | 
				
			||||||
 | 
						ID            string
 | 
				
			||||||
 | 
						Name          string
 | 
				
			||||||
 | 
						Payload       []byte
 | 
				
			||||||
 | 
						NodeFilter    string
 | 
				
			||||||
 | 
						ServiceFilter string
 | 
				
			||||||
 | 
						TagFilter     string
 | 
				
			||||||
 | 
						Version       int
 | 
				
			||||||
 | 
						LTime         uint64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Event returns a handle to the event endpoints
 | 
				
			||||||
 | 
					func (c *Client) Event() *Event {
 | 
				
			||||||
 | 
						return &Event{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fire is used to fire a new user event. Only the Name, Payload and Filters
 | 
				
			||||||
 | 
					// are respected. This returns the ID or an associated error. Cross DC requests
 | 
				
			||||||
 | 
					// are supported.
 | 
				
			||||||
 | 
					func (e *Event) Fire(params *UserEvent, q *WriteOptions) (string, *WriteMeta, error) {
 | 
				
			||||||
 | 
						r := e.c.newRequest("PUT", "/v1/event/fire/"+params.Name)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						if params.NodeFilter != "" {
 | 
				
			||||||
 | 
							r.params.Set("node", params.NodeFilter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if params.ServiceFilter != "" {
 | 
				
			||||||
 | 
							r.params.Set("service", params.ServiceFilter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if params.TagFilter != "" {
 | 
				
			||||||
 | 
							r.params.Set("tag", params.TagFilter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if params.Payload != nil {
 | 
				
			||||||
 | 
							r.body = bytes.NewReader(params.Payload)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(e.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						var out UserEvent
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out.ID, wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List is used to get the most recent events an agent has received.
 | 
				
			||||||
 | 
					// This list can be optionally filtered by the name. This endpoint supports
 | 
				
			||||||
 | 
					// quasi-blocking queries. The index is not monotonic, nor does it provide provide
 | 
				
			||||||
 | 
					// LastContact or KnownLeader.
 | 
				
			||||||
 | 
					func (e *Event) List(name string, q *QueryOptions) ([]*UserEvent, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := e.c.newRequest("GET", "/v1/event/list")
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						if name != "" {
 | 
				
			||||||
 | 
							r.params.Set("name", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(e.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*UserEvent
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IDToIndex is a bit of a hack. This simulates the index generation to
 | 
				
			||||||
 | 
					// convert an event ID into a WaitIndex.
 | 
				
			||||||
 | 
					func (e *Event) IDToIndex(uuid string) uint64 {
 | 
				
			||||||
 | 
						lower := uuid[0:8] + uuid[9:13] + uuid[14:18]
 | 
				
			||||||
 | 
						upper := uuid[19:23] + uuid[24:36]
 | 
				
			||||||
 | 
						lowVal, err := strconv.ParseUint(lower, 16, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic("Failed to convert " + lower)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						highVal, err := strconv.ParseUint(upper, 16, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic("Failed to convert " + upper)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return lowVal ^ highVal
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										136
									
								
								vendor/github.com/armon/consul-api/health.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/armon/consul-api/health.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HealthCheck is used to represent a single check
 | 
				
			||||||
 | 
					type HealthCheck struct {
 | 
				
			||||||
 | 
						Node        string
 | 
				
			||||||
 | 
						CheckID     string
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Status      string
 | 
				
			||||||
 | 
						Notes       string
 | 
				
			||||||
 | 
						Output      string
 | 
				
			||||||
 | 
						ServiceID   string
 | 
				
			||||||
 | 
						ServiceName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ServiceEntry is used for the health service endpoint
 | 
				
			||||||
 | 
					type ServiceEntry struct {
 | 
				
			||||||
 | 
						Node    *Node
 | 
				
			||||||
 | 
						Service *AgentService
 | 
				
			||||||
 | 
						Checks  []*HealthCheck
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Health can be used to query the Health endpoints
 | 
				
			||||||
 | 
					type Health struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Health returns a handle to the health endpoints
 | 
				
			||||||
 | 
					func (c *Client) Health() *Health {
 | 
				
			||||||
 | 
						return &Health{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Node is used to query for checks belonging to a given node
 | 
				
			||||||
 | 
					func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := h.c.newRequest("GET", "/v1/health/node/"+node)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*HealthCheck
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Checks is used to return the checks associated with a service
 | 
				
			||||||
 | 
					func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := h.c.newRequest("GET", "/v1/health/checks/"+service)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*HealthCheck
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Service is used to query health information along with service info
 | 
				
			||||||
 | 
					// for a given service. It can optionally do server-side filtering on a tag
 | 
				
			||||||
 | 
					// or nodes with passing health checks only.
 | 
				
			||||||
 | 
					func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := h.c.newRequest("GET", "/v1/health/service/"+service)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						if tag != "" {
 | 
				
			||||||
 | 
							r.params.Set("tag", tag)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if passingOnly {
 | 
				
			||||||
 | 
							r.params.Set("passing", "1")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*ServiceEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// State is used to retrieve all the checks in a given state.
 | 
				
			||||||
 | 
					// The wildcard "any" state can also be used for all checks.
 | 
				
			||||||
 | 
					func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
 | 
				
			||||||
 | 
						switch state {
 | 
				
			||||||
 | 
						case "any":
 | 
				
			||||||
 | 
						case "warning":
 | 
				
			||||||
 | 
						case "critical":
 | 
				
			||||||
 | 
						case "passing":
 | 
				
			||||||
 | 
						case "unknown":
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, nil, fmt.Errorf("Unsupported state: %v", state)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r := h.c.newRequest("GET", "/v1/health/state/"+state)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(h.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []*HealthCheck
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										219
									
								
								vendor/github.com/armon/consul-api/kv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								vendor/github.com/armon/consul-api/kv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// KVPair is used to represent a single K/V entry
 | 
				
			||||||
 | 
					type KVPair struct {
 | 
				
			||||||
 | 
						Key         string
 | 
				
			||||||
 | 
						CreateIndex uint64
 | 
				
			||||||
 | 
						ModifyIndex uint64
 | 
				
			||||||
 | 
						LockIndex   uint64
 | 
				
			||||||
 | 
						Flags       uint64
 | 
				
			||||||
 | 
						Value       []byte
 | 
				
			||||||
 | 
						Session     string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// KVPairs is a list of KVPair objects
 | 
				
			||||||
 | 
					type KVPairs []*KVPair
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// KV is used to manipulate the K/V API
 | 
				
			||||||
 | 
					type KV struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// KV is used to return a handle to the K/V apis
 | 
				
			||||||
 | 
					func (c *Client) KV() *KV {
 | 
				
			||||||
 | 
						return &KV{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get is used to lookup a single key
 | 
				
			||||||
 | 
					func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error) {
 | 
				
			||||||
 | 
						resp, qm, err := k.getInternal(key, nil, q)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp == nil {
 | 
				
			||||||
 | 
							return nil, qm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*KVPair
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(entries) > 0 {
 | 
				
			||||||
 | 
							return entries[0], qm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List is used to lookup all keys under a prefix
 | 
				
			||||||
 | 
					func (k *KV) List(prefix string, q *QueryOptions) (KVPairs, *QueryMeta, error) {
 | 
				
			||||||
 | 
						resp, qm, err := k.getInternal(prefix, map[string]string{"recurse": ""}, q)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp == nil {
 | 
				
			||||||
 | 
							return nil, qm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*KVPair
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Keys is used to list all the keys under a prefix. Optionally,
 | 
				
			||||||
 | 
					// a separator can be used to limit the responses.
 | 
				
			||||||
 | 
					func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMeta, error) {
 | 
				
			||||||
 | 
						params := map[string]string{"keys": ""}
 | 
				
			||||||
 | 
						if separator != "" {
 | 
				
			||||||
 | 
							params["separator"] = separator
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp, qm, err := k.getInternal(prefix, params, q)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp == nil {
 | 
				
			||||||
 | 
							return nil, qm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []string
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := k.c.newRequest("GET", "/v1/kv/"+key)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						for param, val := range params {
 | 
				
			||||||
 | 
							r.params.Set(param, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rtt, resp, err := k.c.doRequest(r)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.StatusCode == 404 {
 | 
				
			||||||
 | 
							resp.Body.Close()
 | 
				
			||||||
 | 
							return nil, qm, nil
 | 
				
			||||||
 | 
						} else if resp.StatusCode != 200 {
 | 
				
			||||||
 | 
							resp.Body.Close()
 | 
				
			||||||
 | 
							return nil, nil, fmt.Errorf("Unexpected response code: %d", resp.StatusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Put is used to write a new value. Only the
 | 
				
			||||||
 | 
					// Key, Flags and Value is respected.
 | 
				
			||||||
 | 
					func (k *KV) Put(p *KVPair, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						params := make(map[string]string, 1)
 | 
				
			||||||
 | 
						if p.Flags != 0 {
 | 
				
			||||||
 | 
							params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, wm, err := k.put(p.Key, params, p.Value, q)
 | 
				
			||||||
 | 
						return wm, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CAS is used for a Check-And-Set operation. The Key,
 | 
				
			||||||
 | 
					// ModifyIndex, Flags and Value are respected. Returns true
 | 
				
			||||||
 | 
					// on success or false on failures.
 | 
				
			||||||
 | 
					func (k *KV) CAS(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
				
			||||||
 | 
						params := make(map[string]string, 2)
 | 
				
			||||||
 | 
						if p.Flags != 0 {
 | 
				
			||||||
 | 
							params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						params["cas"] = strconv.FormatUint(p.ModifyIndex, 10)
 | 
				
			||||||
 | 
						return k.put(p.Key, params, p.Value, q)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Acquire is used for a lock acquisiiton operation. The Key,
 | 
				
			||||||
 | 
					// Flags, Value and Session are respected. Returns true
 | 
				
			||||||
 | 
					// on success or false on failures.
 | 
				
			||||||
 | 
					func (k *KV) Acquire(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
				
			||||||
 | 
						params := make(map[string]string, 2)
 | 
				
			||||||
 | 
						if p.Flags != 0 {
 | 
				
			||||||
 | 
							params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						params["acquire"] = p.Session
 | 
				
			||||||
 | 
						return k.put(p.Key, params, p.Value, q)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Release is used for a lock release operation. The Key,
 | 
				
			||||||
 | 
					// Flags, Value and Session are respected. Returns true
 | 
				
			||||||
 | 
					// on success or false on failures.
 | 
				
			||||||
 | 
					func (k *KV) Release(p *KVPair, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
				
			||||||
 | 
						params := make(map[string]string, 2)
 | 
				
			||||||
 | 
						if p.Flags != 0 {
 | 
				
			||||||
 | 
							params["flags"] = strconv.FormatUint(p.Flags, 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						params["release"] = p.Session
 | 
				
			||||||
 | 
						return k.put(p.Key, params, p.Value, q)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KV) put(key string, params map[string]string, body []byte, q *WriteOptions) (bool, *WriteMeta, error) {
 | 
				
			||||||
 | 
						r := k.c.newRequest("PUT", "/v1/kv/"+key)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						for param, val := range params {
 | 
				
			||||||
 | 
							r.params.Set(param, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r.body = bytes.NewReader(body)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(k.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &WriteMeta{}
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var buf bytes.Buffer
 | 
				
			||||||
 | 
						if _, err := io.Copy(&buf, resp.Body); err != nil {
 | 
				
			||||||
 | 
							return false, nil, fmt.Errorf("Failed to read response: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := strings.Contains(string(buf.Bytes()), "true")
 | 
				
			||||||
 | 
						return res, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Delete is used to delete a single key
 | 
				
			||||||
 | 
					func (k *KV) Delete(key string, w *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						return k.deleteInternal(key, nil, w)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteTree is used to delete all keys under a prefix
 | 
				
			||||||
 | 
					func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						return k.deleteInternal(prefix, []string{"recurse"}, w)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KV) deleteInternal(key string, params []string, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						r := k.c.newRequest("DELETE", "/v1/kv/"+key)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						for _, param := range params {
 | 
				
			||||||
 | 
							r.params.Set(param, "")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(k.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &WriteMeta{}
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
						return qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										204
									
								
								vendor/github.com/armon/consul-api/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								vendor/github.com/armon/consul-api/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SessionEntry represents a session in consul
 | 
				
			||||||
 | 
					type SessionEntry struct {
 | 
				
			||||||
 | 
						CreateIndex uint64
 | 
				
			||||||
 | 
						ID          string
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Node        string
 | 
				
			||||||
 | 
						Checks      []string
 | 
				
			||||||
 | 
						LockDelay   time.Duration
 | 
				
			||||||
 | 
						Behavior    string
 | 
				
			||||||
 | 
						TTL         string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Session can be used to query the Session endpoints
 | 
				
			||||||
 | 
					type Session struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Session returns a handle to the session endpoints
 | 
				
			||||||
 | 
					func (c *Client) Session() *Session {
 | 
				
			||||||
 | 
						return &Session{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateNoChecks is like Create but is used specifically to create
 | 
				
			||||||
 | 
					// a session with no associated health checks.
 | 
				
			||||||
 | 
					func (s *Session) CreateNoChecks(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) {
 | 
				
			||||||
 | 
						body := make(map[string]interface{})
 | 
				
			||||||
 | 
						body["Checks"] = []string{}
 | 
				
			||||||
 | 
						if se != nil {
 | 
				
			||||||
 | 
							if se.Name != "" {
 | 
				
			||||||
 | 
								body["Name"] = se.Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.Node != "" {
 | 
				
			||||||
 | 
								body["Node"] = se.Node
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.LockDelay != 0 {
 | 
				
			||||||
 | 
								body["LockDelay"] = durToMsec(se.LockDelay)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.Behavior != "" {
 | 
				
			||||||
 | 
								body["Behavior"] = se.Behavior
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.TTL != "" {
 | 
				
			||||||
 | 
								body["TTL"] = se.TTL
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s.create(body, q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create makes a new session. Providing a session entry can
 | 
				
			||||||
 | 
					// customize the session. It can also be nil to use defaults.
 | 
				
			||||||
 | 
					func (s *Session) Create(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) {
 | 
				
			||||||
 | 
						var obj interface{}
 | 
				
			||||||
 | 
						if se != nil {
 | 
				
			||||||
 | 
							body := make(map[string]interface{})
 | 
				
			||||||
 | 
							obj = body
 | 
				
			||||||
 | 
							if se.Name != "" {
 | 
				
			||||||
 | 
								body["Name"] = se.Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.Node != "" {
 | 
				
			||||||
 | 
								body["Node"] = se.Node
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.LockDelay != 0 {
 | 
				
			||||||
 | 
								body["LockDelay"] = durToMsec(se.LockDelay)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(se.Checks) > 0 {
 | 
				
			||||||
 | 
								body["Checks"] = se.Checks
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.Behavior != "" {
 | 
				
			||||||
 | 
								body["Behavior"] = se.Behavior
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if se.TTL != "" {
 | 
				
			||||||
 | 
								body["TTL"] = se.TTL
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s.create(obj, q)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) create(obj interface{}, q *WriteOptions) (string, *WriteMeta, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("PUT", "/v1/session/create")
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						r.obj = obj
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						var out struct{ ID string }
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &out); err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out.ID, wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Destroy invalides a given session
 | 
				
			||||||
 | 
					func (s *Session) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("PUT", "/v1/session/destroy/"+id)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
						return wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Renew renews the TTL on a given session
 | 
				
			||||||
 | 
					func (s *Session) Renew(id string, q *WriteOptions) (*SessionEntry, *WriteMeta, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("PUT", "/v1/session/renew/"+id)
 | 
				
			||||||
 | 
						r.setWriteOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wm := &WriteMeta{RequestTime: rtt}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*SessionEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, wm, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(entries) > 0 {
 | 
				
			||||||
 | 
							return entries[0], wm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, wm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Info looks up a single session
 | 
				
			||||||
 | 
					func (s *Session) Info(id string, q *QueryOptions) (*SessionEntry, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("GET", "/v1/session/info/"+id)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*SessionEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(entries) > 0 {
 | 
				
			||||||
 | 
							return entries[0], qm, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List gets sessions for a node
 | 
				
			||||||
 | 
					func (s *Session) Node(node string, q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("GET", "/v1/session/node/"+node)
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*SessionEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List gets all active sessions
 | 
				
			||||||
 | 
					func (s *Session) List(q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("GET", "/v1/session/list")
 | 
				
			||||||
 | 
						r.setQueryOptions(q)
 | 
				
			||||||
 | 
						rtt, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qm := &QueryMeta{}
 | 
				
			||||||
 | 
						parseQueryMeta(resp, qm)
 | 
				
			||||||
 | 
						qm.RequestTime = rtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var entries []*SessionEntry
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &entries); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries, qm, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								vendor/github.com/armon/consul-api/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/armon/consul-api/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					package consulapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Status can be used to query the Status endpoints
 | 
				
			||||||
 | 
					type Status struct {
 | 
				
			||||||
 | 
						c *Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Status returns a handle to the status endpoints
 | 
				
			||||||
 | 
					func (c *Client) Status() *Status {
 | 
				
			||||||
 | 
						return &Status{c}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Leader is used to query for a known leader
 | 
				
			||||||
 | 
					func (s *Status) Leader() (string, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("GET", "/v1/status/leader")
 | 
				
			||||||
 | 
						_, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var leader string
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &leader); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return leader, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Peers is used to query for a known raft peers
 | 
				
			||||||
 | 
					func (s *Status) Peers() ([]string, error) {
 | 
				
			||||||
 | 
						r := s.c.newRequest("GET", "/v1/status/peers")
 | 
				
			||||||
 | 
						_, resp, err := requireOK(s.c.doRequest(r))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var peers []string
 | 
				
			||||||
 | 
						if err := decodeBody(resp, &peers); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return peers, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										4
									
								
								vendor/github.com/bwmarrin/discordgo/discord.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/bwmarrin/discordgo/discord.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
 | 
					// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
 | 
				
			||||||
const VERSION = "0.17.0"
 | 
					const VERSION = "0.18.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrMFA will be risen by New when the user has 2FA.
 | 
					// ErrMFA will be risen by New when the user has 2FA.
 | 
				
			||||||
var ErrMFA = errors.New("account has 2FA enabled")
 | 
					var ErrMFA = errors.New("account has 2FA enabled")
 | 
				
			||||||
@@ -50,7 +50,7 @@ func New(args ...interface{}) (s *Session, err error) {
 | 
				
			|||||||
	// Create an empty Session interface.
 | 
						// Create an empty Session interface.
 | 
				
			||||||
	s = &Session{
 | 
						s = &Session{
 | 
				
			||||||
		State:                  NewState(),
 | 
							State:                  NewState(),
 | 
				
			||||||
		ratelimiter:            NewRatelimiter(),
 | 
							Ratelimiter:            NewRatelimiter(),
 | 
				
			||||||
		StateEnabled:           true,
 | 
							StateEnabled:           true,
 | 
				
			||||||
		Compress:               true,
 | 
							Compress:               true,
 | 
				
			||||||
		ShouldReconnectOnError: true,
 | 
							ShouldReconnectOnError: true,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								vendor/github.com/bwmarrin/discordgo/endpoints.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/bwmarrin/discordgo/endpoints.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -71,7 +71,6 @@ var (
 | 
				
			|||||||
	EndpointUserNotes          = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
 | 
						EndpointUserNotes          = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EndpointGuild                = func(gID string) string { return EndpointGuilds + gID }
 | 
						EndpointGuild                = func(gID string) string { return EndpointGuilds + gID }
 | 
				
			||||||
	EndpointGuildInivtes         = func(gID string) string { return EndpointGuilds + gID + "/invites" }
 | 
					 | 
				
			||||||
	EndpointGuildChannels        = func(gID string) string { return EndpointGuilds + gID + "/channels" }
 | 
						EndpointGuildChannels        = func(gID string) string { return EndpointGuilds + gID + "/channels" }
 | 
				
			||||||
	EndpointGuildMembers         = func(gID string) string { return EndpointGuilds + gID + "/members" }
 | 
						EndpointGuildMembers         = func(gID string) string { return EndpointGuilds + gID + "/members" }
 | 
				
			||||||
	EndpointGuildMember          = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
 | 
						EndpointGuildMember          = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
 | 
				
			||||||
@@ -98,7 +97,7 @@ var (
 | 
				
			|||||||
	EndpointChannelMessages           = func(cID string) string { return EndpointChannels + cID + "/messages" }
 | 
						EndpointChannelMessages           = func(cID string) string { return EndpointChannels + cID + "/messages" }
 | 
				
			||||||
	EndpointChannelMessage            = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
 | 
						EndpointChannelMessage            = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
 | 
				
			||||||
	EndpointChannelMessageAck         = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
 | 
						EndpointChannelMessageAck         = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
 | 
				
			||||||
	EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" }
 | 
						EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
 | 
				
			||||||
	EndpointChannelMessagesPins       = func(cID string) string { return EndpointChannel(cID) + "/pins" }
 | 
						EndpointChannelMessagesPins       = func(cID string) string { return EndpointChannel(cID) + "/pins" }
 | 
				
			||||||
	EndpointChannelMessagePin         = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
 | 
						EndpointChannelMessagePin         = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,6 +121,8 @@ var (
 | 
				
			|||||||
	EndpointRelationship        = func(uID string) string { return EndpointRelationships() + "/" + uID }
 | 
						EndpointRelationship        = func(uID string) string { return EndpointRelationships() + "/" + uID }
 | 
				
			||||||
	EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
 | 
						EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EndpointGuildCreate = EndpointAPI + "guilds"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
 | 
						EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
 | 
						EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@ type EventHandler interface {
 | 
				
			|||||||
	Type() string
 | 
						Type() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle is called whenever an event of Type() happens.
 | 
						// Handle is called whenever an event of Type() happens.
 | 
				
			||||||
	// It is the recievers responsibility to type assert that the interface
 | 
						// It is the receivers responsibility to type assert that the interface
 | 
				
			||||||
	// is the expected struct.
 | 
						// is the expected struct.
 | 
				
			||||||
	Handle(*Session, interface{})
 | 
						Handle(*Session, interface{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -79,7 +79,7 @@ func main() {
 | 
				
			|||||||
	ap.Name = Name
 | 
						ap.Name = Name
 | 
				
			||||||
	ap, err = dg.ApplicationCreate(ap)
 | 
						ap, err = dg.ApplicationCreate(ap)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println("error creating new applicaiton,", err)
 | 
							fmt.Println("error creating new application,", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								vendor/github.com/bwmarrin/discordgo/logging.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/bwmarrin/discordgo/logging.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ const (
 | 
				
			|||||||
	LogError int = iota
 | 
						LogError int = iota
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// LogWarning level is used for very abnormal events and errors that are
 | 
						// LogWarning level is used for very abnormal events and errors that are
 | 
				
			||||||
	// also returend to a calling function.
 | 
						// also returned to a calling function.
 | 
				
			||||||
	LogWarning
 | 
						LogWarning
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// LogInformational level is used for normal non-error activity
 | 
						// LogInformational level is used for normal non-error activity
 | 
				
			||||||
@@ -34,26 +34,34 @@ const (
 | 
				
			|||||||
	LogDebug
 | 
						LogDebug
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Logger can be used to replace the standard logging for discordgo
 | 
				
			||||||
 | 
					var Logger func(msgL, caller int, format string, a ...interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// msglog provides package wide logging consistancy for discordgo
 | 
					// msglog provides package wide logging consistancy for discordgo
 | 
				
			||||||
// the format, a...  portion this command follows that of fmt.Printf
 | 
					// the format, a...  portion this command follows that of fmt.Printf
 | 
				
			||||||
//   msgL   : LogLevel of the message
 | 
					//   msgL   : LogLevel of the message
 | 
				
			||||||
//   caller : 1 + the number of callers away from the message source
 | 
					//   caller : 1 + the number of callers away from the message source
 | 
				
			||||||
//   format : Printf style message format
 | 
					//   format : Printf style message format
 | 
				
			||||||
//   a ...  : comma seperated list of values to pass
 | 
					//   a ...  : comma separated list of values to pass
 | 
				
			||||||
func msglog(msgL, caller int, format string, a ...interface{}) {
 | 
					func msglog(msgL, caller int, format string, a ...interface{}) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pc, file, line, _ := runtime.Caller(caller)
 | 
						if Logger != nil {
 | 
				
			||||||
 | 
							Logger(msgL, caller, format, a...)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	files := strings.Split(file, "/")
 | 
							pc, file, line, _ := runtime.Caller(caller)
 | 
				
			||||||
	file = files[len(files)-1]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	name := runtime.FuncForPC(pc).Name()
 | 
							files := strings.Split(file, "/")
 | 
				
			||||||
	fns := strings.Split(name, ".")
 | 
							file = files[len(files)-1]
 | 
				
			||||||
	name = fns[len(fns)-1]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	msg := fmt.Sprintf(format, a...)
 | 
							name := runtime.FuncForPC(pc).Name()
 | 
				
			||||||
 | 
							fns := strings.Split(name, ".")
 | 
				
			||||||
 | 
							name = fns[len(fns)-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
 | 
							msg := fmt.Sprintf(format, a...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// helper function that wraps msglog for the Session struct
 | 
					// helper function that wraps msglog for the Session struct
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -237,7 +237,7 @@ func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, e
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		content = strings.Replace(content, "<&"+role.ID+">", "@"+role.Name, -1)
 | 
							content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
 | 
						content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										49
									
								
								vendor/github.com/bwmarrin/discordgo/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/bwmarrin/discordgo/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -41,8 +41,8 @@ func NewRatelimiter() *RateLimiter {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getBucket retrieves or creates a bucket
 | 
					// GetBucket retrieves or creates a bucket
 | 
				
			||||||
func (r *RateLimiter) getBucket(key string) *Bucket {
 | 
					func (r *RateLimiter) GetBucket(key string) *Bucket {
 | 
				
			||||||
	r.Lock()
 | 
						r.Lock()
 | 
				
			||||||
	defer r.Unlock()
 | 
						defer r.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,7 +51,7 @@ func (r *RateLimiter) getBucket(key string) *Bucket {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b := &Bucket{
 | 
						b := &Bucket{
 | 
				
			||||||
		remaining: 1,
 | 
							Remaining: 1,
 | 
				
			||||||
		Key:       key,
 | 
							Key:       key,
 | 
				
			||||||
		global:    r.global,
 | 
							global:    r.global,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -68,27 +68,37 @@ func (r *RateLimiter) getBucket(key string) *Bucket {
 | 
				
			|||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LockBucket Locks until a request can be made
 | 
					// GetWaitTime returns the duration you should wait for a Bucket
 | 
				
			||||||
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
 | 
					func (r *RateLimiter) GetWaitTime(b *Bucket, minRemaining int) time.Duration {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	b := r.getBucket(bucketID)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	b.Lock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// If we ran out of calls and the reset time is still ahead of us
 | 
						// If we ran out of calls and the reset time is still ahead of us
 | 
				
			||||||
	// then we need to take it easy and relax a little
 | 
						// then we need to take it easy and relax a little
 | 
				
			||||||
	if b.remaining < 1 && b.reset.After(time.Now()) {
 | 
						if b.Remaining < minRemaining && b.reset.After(time.Now()) {
 | 
				
			||||||
		time.Sleep(b.reset.Sub(time.Now()))
 | 
							return b.reset.Sub(time.Now())
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check for global ratelimits
 | 
						// Check for global ratelimits
 | 
				
			||||||
	sleepTo := time.Unix(0, atomic.LoadInt64(r.global))
 | 
						sleepTo := time.Unix(0, atomic.LoadInt64(r.global))
 | 
				
			||||||
	if now := time.Now(); now.Before(sleepTo) {
 | 
						if now := time.Now(); now.Before(sleepTo) {
 | 
				
			||||||
		time.Sleep(sleepTo.Sub(now))
 | 
							return sleepTo.Sub(now)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.remaining--
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LockBucket Locks until a request can be made
 | 
				
			||||||
 | 
					func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
 | 
				
			||||||
 | 
						return r.LockBucketObject(r.GetBucket(bucketID))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LockBucketObject Locks an already resolved bucket until a request can be made
 | 
				
			||||||
 | 
					func (r *RateLimiter) LockBucketObject(b *Bucket) *Bucket {
 | 
				
			||||||
 | 
						b.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if wait := r.GetWaitTime(b, 1); wait > 0 {
 | 
				
			||||||
 | 
							time.Sleep(wait)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.Remaining--
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,13 +106,14 @@ func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
 | 
				
			|||||||
type Bucket struct {
 | 
					type Bucket struct {
 | 
				
			||||||
	sync.Mutex
 | 
						sync.Mutex
 | 
				
			||||||
	Key       string
 | 
						Key       string
 | 
				
			||||||
	remaining int
 | 
						Remaining int
 | 
				
			||||||
	limit     int
 | 
						limit     int
 | 
				
			||||||
	reset     time.Time
 | 
						reset     time.Time
 | 
				
			||||||
	global    *int64
 | 
						global    *int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lastReset       time.Time
 | 
						lastReset       time.Time
 | 
				
			||||||
	customRateLimit *customRateLimit
 | 
						customRateLimit *customRateLimit
 | 
				
			||||||
 | 
						Userdata        interface{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
 | 
					// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
 | 
				
			||||||
@@ -113,10 +124,10 @@ func (b *Bucket) Release(headers http.Header) error {
 | 
				
			|||||||
	// Check if the bucket uses a custom ratelimiter
 | 
						// Check if the bucket uses a custom ratelimiter
 | 
				
			||||||
	if rl := b.customRateLimit; rl != nil {
 | 
						if rl := b.customRateLimit; rl != nil {
 | 
				
			||||||
		if time.Now().Sub(b.lastReset) >= rl.reset {
 | 
							if time.Now().Sub(b.lastReset) >= rl.reset {
 | 
				
			||||||
			b.remaining = rl.requests - 1
 | 
								b.Remaining = rl.requests - 1
 | 
				
			||||||
			b.lastReset = time.Now()
 | 
								b.lastReset = time.Now()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if b.remaining < 1 {
 | 
							if b.Remaining < 1 {
 | 
				
			||||||
			b.reset = time.Now().Add(rl.reset)
 | 
								b.reset = time.Now().Add(rl.reset)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -176,7 +187,7 @@ func (b *Bucket) Release(headers http.Header) error {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		b.remaining = int(parsedRemaining)
 | 
							b.Remaining = int(parsedRemaining)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										54
									
								
								vendor/github.com/bwmarrin/discordgo/restapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/bwmarrin/discordgo/restapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -65,9 +65,11 @@ func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID
 | 
				
			|||||||
	if bucketID == "" {
 | 
						if bucketID == "" {
 | 
				
			||||||
		bucketID = strings.SplitN(urlStr, "?", 2)[0]
 | 
							bucketID = strings.SplitN(urlStr, "?", 2)[0]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucket(bucketID), sequence)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bucket := s.ratelimiter.LockBucket(bucketID)
 | 
					// RequestWithLockedBucket makes a request using a bucket that's already been locked
 | 
				
			||||||
 | 
					func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b []byte, bucket *Bucket, sequence int) (response []byte, err error) {
 | 
				
			||||||
	if s.Debug {
 | 
						if s.Debug {
 | 
				
			||||||
		log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
 | 
							log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
 | 
				
			||||||
		log.Printf("API REQUEST  PAYLOAD :: [%s]\n", string(b))
 | 
							log.Printf("API REQUEST  PAYLOAD :: [%s]\n", string(b))
 | 
				
			||||||
@@ -139,7 +141,7 @@ func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID
 | 
				
			|||||||
		if sequence < s.MaxRestRetries {
 | 
							if sequence < s.MaxRestRetries {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status)
 | 
								s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status)
 | 
				
			||||||
			response, err = s.request(method, urlStr, contentType, b, bucketID, sequence+1)
 | 
								response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence+1)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response)
 | 
								err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -158,7 +160,7 @@ func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID
 | 
				
			|||||||
		// we can make the above smarter
 | 
							// we can make the above smarter
 | 
				
			||||||
		// this method can cause longer delays than required
 | 
							// this method can cause longer delays than required
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		response, err = s.request(method, urlStr, contentType, b, bucketID, sequence)
 | 
							response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default: // Error condition
 | 
						default: // Error condition
 | 
				
			||||||
		err = newRestError(req, resp, response)
 | 
							err = newRestError(req, resp, response)
 | 
				
			||||||
@@ -585,7 +587,7 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) {
 | 
				
			|||||||
		Name string `json:"name"`
 | 
							Name string `json:"name"`
 | 
				
			||||||
	}{name}
 | 
						}{name}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := s.RequestWithBucketID("POST", EndpointGuilds, data, EndpointGuilds)
 | 
						body, err := s.RequestWithBucketID("POST", EndpointGuildCreate, data, EndpointGuildCreate)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -907,7 +909,7 @@ func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err
 | 
				
			|||||||
// GuildInvites returns an array of Invite structures for the given guild
 | 
					// GuildInvites returns an array of Invite structures for the given guild
 | 
				
			||||||
// guildID   : The ID of a Guild.
 | 
					// guildID   : The ID of a Guild.
 | 
				
			||||||
func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) {
 | 
					func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) {
 | 
				
			||||||
	body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInivtes(guildID))
 | 
						body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInvites(guildID))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -957,6 +959,7 @@ func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist b
 | 
				
			|||||||
	// Prevent sending a color int that is too big.
 | 
						// Prevent sending a color int that is too big.
 | 
				
			||||||
	if color > 0xFFFFFF {
 | 
						if color > 0xFFFFFF {
 | 
				
			||||||
		err = fmt.Errorf("color value cannot be larger than 0xFFFFFF")
 | 
							err = fmt.Errorf("color value cannot be larger than 0xFFFFFF")
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := struct {
 | 
						data := struct {
 | 
				
			||||||
@@ -1020,6 +1023,9 @@ func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, er
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	uri := EndpointGuildPrune(guildID) + fmt.Sprintf("?days=%d", days)
 | 
						uri := EndpointGuildPrune(guildID) + fmt.Sprintf("?days=%d", days)
 | 
				
			||||||
	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID))
 | 
						body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = unmarshal(body, &p)
 | 
						err = unmarshal(body, &p)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -1204,7 +1210,7 @@ func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string)
 | 
				
			|||||||
// Functions specific to Discord Channels
 | 
					// Functions specific to Discord Channels
 | 
				
			||||||
// ------------------------------------------------------------------------------------------------
 | 
					// ------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Channel returns a Channel strucutre of a specific Channel.
 | 
					// Channel returns a Channel structure of a specific Channel.
 | 
				
			||||||
// channelID  : The ID of the Channel you want returned.
 | 
					// channelID  : The ID of the Channel you want returned.
 | 
				
			||||||
func (s *Session) Channel(channelID string) (st *Channel, err error) {
 | 
					func (s *Session) Channel(channelID string) (st *Channel, err error) {
 | 
				
			||||||
	body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID))
 | 
						body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID))
 | 
				
			||||||
@@ -1219,12 +1225,16 @@ func (s *Session) Channel(channelID string) (st *Channel, err error) {
 | 
				
			|||||||
// ChannelEdit edits the given channel
 | 
					// ChannelEdit edits the given channel
 | 
				
			||||||
// channelID  : The ID of a Channel
 | 
					// channelID  : The ID of a Channel
 | 
				
			||||||
// name       : The new name to assign the channel.
 | 
					// name       : The new name to assign the channel.
 | 
				
			||||||
func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) {
 | 
					func (s *Session) ChannelEdit(channelID, name string) (*Channel, error) {
 | 
				
			||||||
 | 
						return s.ChannelEditComplex(channelID, &ChannelEdit{
 | 
				
			||||||
	data := struct {
 | 
							Name: name,
 | 
				
			||||||
		Name string `json:"name"`
 | 
						})
 | 
				
			||||||
	}{name}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ChannelEditComplex edits an existing channel, replacing the parameters entirely with ChannelEdit struct
 | 
				
			||||||
 | 
					// channelID  : The ID of a Channel
 | 
				
			||||||
 | 
					// data          : The channel struct to send
 | 
				
			||||||
 | 
					func (s *Session) ChannelEditComplex(channelID string, data *ChannelEdit) (st *Channel, err error) {
 | 
				
			||||||
	body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID))
 | 
						body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -1476,7 +1486,7 @@ func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs.
 | 
					// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs.
 | 
				
			||||||
// If only one messageID is in the slice call channelMessageDelete funciton.
 | 
					// If only one messageID is in the slice call channelMessageDelete function.
 | 
				
			||||||
// If the slice is empty do nothing.
 | 
					// If the slice is empty do nothing.
 | 
				
			||||||
// channelID : The ID of the channel for the messages to delete.
 | 
					// channelID : The ID of the channel for the messages to delete.
 | 
				
			||||||
// messages  : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages.
 | 
					// messages  : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages.
 | 
				
			||||||
@@ -1569,16 +1579,14 @@ func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ChannelInviteCreate creates a new invite for the given channel.
 | 
					// ChannelInviteCreate creates a new invite for the given channel.
 | 
				
			||||||
// channelID   : The ID of a Channel
 | 
					// channelID   : The ID of a Channel
 | 
				
			||||||
// i           : An Invite struct with the values MaxAge, MaxUses, Temporary,
 | 
					// i           : An Invite struct with the values MaxAge, MaxUses and Temporary defined.
 | 
				
			||||||
//               and XkcdPass defined.
 | 
					 | 
				
			||||||
func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) {
 | 
					func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := struct {
 | 
						data := struct {
 | 
				
			||||||
		MaxAge    int    `json:"max_age"`
 | 
							MaxAge    int  `json:"max_age"`
 | 
				
			||||||
		MaxUses   int    `json:"max_uses"`
 | 
							MaxUses   int  `json:"max_uses"`
 | 
				
			||||||
		Temporary bool   `json:"temporary"`
 | 
							Temporary bool `json:"temporary"`
 | 
				
			||||||
		XKCDPass  string `json:"xkcdpass"`
 | 
						}{i.MaxAge, i.MaxUses, i.Temporary}
 | 
				
			||||||
	}{i.MaxAge, i.MaxUses, i.Temporary, i.XkcdPass}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID))
 | 
						body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -1618,7 +1626,7 @@ func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error
 | 
				
			|||||||
// ------------------------------------------------------------------------------------------------
 | 
					// ------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Invite returns an Invite structure of the given invite
 | 
					// Invite returns an Invite structure of the given invite
 | 
				
			||||||
// inviteID : The invite code (or maybe xkcdpass?)
 | 
					// inviteID : The invite code
 | 
				
			||||||
func (s *Session) Invite(inviteID string) (st *Invite, err error) {
 | 
					func (s *Session) Invite(inviteID string) (st *Invite, err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
						body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
				
			||||||
@@ -1631,7 +1639,7 @@ func (s *Session) Invite(inviteID string) (st *Invite, err error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InviteDelete deletes an existing invite
 | 
					// InviteDelete deletes an existing invite
 | 
				
			||||||
// inviteID   : the code (or maybe xkcdpass?) of an invite
 | 
					// inviteID   : the code of an invite
 | 
				
			||||||
func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
 | 
					func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
						body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
				
			||||||
@@ -1644,7 +1652,7 @@ func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InviteAccept accepts an Invite to a Guild or Channel
 | 
					// InviteAccept accepts an Invite to a Guild or Channel
 | 
				
			||||||
// inviteID : The invite code (or maybe xkcdpass?)
 | 
					// inviteID : The invite code
 | 
				
			||||||
func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) {
 | 
					func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
						body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/state.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/state.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -531,7 +531,7 @@ func (s *State) PrivateChannel(channelID string) (*Channel, error) {
 | 
				
			|||||||
	return s.Channel(channelID)
 | 
						return s.Channel(channelID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Channel gets a channel by ID, it will look in all guilds an private channels.
 | 
					// Channel gets a channel by ID, it will look in all guilds and private channels.
 | 
				
			||||||
func (s *State) Channel(channelID string) (*Channel, error) {
 | 
					func (s *State) Channel(channelID string) (*Channel, error) {
 | 
				
			||||||
	if s == nil {
 | 
						if s == nil {
 | 
				
			||||||
		return nil, ErrNilState
 | 
							return nil, ErrNilState
 | 
				
			||||||
@@ -816,6 +816,13 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
 | 
				
			|||||||
		if s.TrackMembers {
 | 
							if s.TrackMembers {
 | 
				
			||||||
			err = s.MemberRemove(t.Member)
 | 
								err = s.MemberRemove(t.Member)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						case *GuildMembersChunk:
 | 
				
			||||||
 | 
							if s.TrackMembers {
 | 
				
			||||||
 | 
								for i := range t.Members {
 | 
				
			||||||
 | 
									t.Members[i].GuildID = t.GuildID
 | 
				
			||||||
 | 
									err = s.MemberAdd(t.Members[i])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	case *GuildRoleCreate:
 | 
						case *GuildRoleCreate:
 | 
				
			||||||
		if s.TrackRoles {
 | 
							if s.TrackRoles {
 | 
				
			||||||
			err = s.RoleAdd(t.GuildID, t.Role)
 | 
								err = s.RoleAdd(t.GuildID, t.Role)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								vendor/github.com/bwmarrin/discordgo/structs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										98
									
								
								vendor/github.com/bwmarrin/discordgo/structs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -14,7 +14,6 @@ package discordgo
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,6 +84,9 @@ type Session struct {
 | 
				
			|||||||
	// Stores the last HeartbeatAck that was recieved (in UTC)
 | 
						// Stores the last HeartbeatAck that was recieved (in UTC)
 | 
				
			||||||
	LastHeartbeatAck time.Time
 | 
						LastHeartbeatAck time.Time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// used to deal with rate limits
 | 
				
			||||||
 | 
						Ratelimiter *RateLimiter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Event handlers
 | 
						// Event handlers
 | 
				
			||||||
	handlersMu   sync.RWMutex
 | 
						handlersMu   sync.RWMutex
 | 
				
			||||||
	handlers     map[string][]*eventHandlerInstance
 | 
						handlers     map[string][]*eventHandlerInstance
 | 
				
			||||||
@@ -96,9 +98,6 @@ type Session struct {
 | 
				
			|||||||
	// When nil, the session is not listening.
 | 
						// When nil, the session is not listening.
 | 
				
			||||||
	listening chan interface{}
 | 
						listening chan interface{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// used to deal with rate limits
 | 
					 | 
				
			||||||
	ratelimiter *RateLimiter
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// sequence tracks the current gateway api websocket sequence number
 | 
						// sequence tracks the current gateway api websocket sequence number
 | 
				
			||||||
	sequence *int64
 | 
						sequence *int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,9 +142,9 @@ type Invite struct {
 | 
				
			|||||||
	MaxAge    int       `json:"max_age"`
 | 
						MaxAge    int       `json:"max_age"`
 | 
				
			||||||
	Uses      int       `json:"uses"`
 | 
						Uses      int       `json:"uses"`
 | 
				
			||||||
	MaxUses   int       `json:"max_uses"`
 | 
						MaxUses   int       `json:"max_uses"`
 | 
				
			||||||
	XkcdPass  string    `json:"xkcdpass"`
 | 
					 | 
				
			||||||
	Revoked   bool      `json:"revoked"`
 | 
						Revoked   bool      `json:"revoked"`
 | 
				
			||||||
	Temporary bool      `json:"temporary"`
 | 
						Temporary bool      `json:"temporary"`
 | 
				
			||||||
 | 
						Unique    bool      `json:"unique"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ChannelType is the type of a Channel
 | 
					// ChannelType is the type of a Channel
 | 
				
			||||||
@@ -171,9 +170,22 @@ type Channel struct {
 | 
				
			|||||||
	NSFW                 bool                   `json:"nsfw"`
 | 
						NSFW                 bool                   `json:"nsfw"`
 | 
				
			||||||
	Position             int                    `json:"position"`
 | 
						Position             int                    `json:"position"`
 | 
				
			||||||
	Bitrate              int                    `json:"bitrate"`
 | 
						Bitrate              int                    `json:"bitrate"`
 | 
				
			||||||
	Recipients           []*User                `json:"recipient"`
 | 
						Recipients           []*User                `json:"recipients"`
 | 
				
			||||||
	Messages             []*Message             `json:"-"`
 | 
						Messages             []*Message             `json:"-"`
 | 
				
			||||||
	PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
 | 
						PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
 | 
				
			||||||
 | 
						ParentID             string                 `json:"parent_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A ChannelEdit holds Channel Feild data for a channel edit.
 | 
				
			||||||
 | 
					type ChannelEdit struct {
 | 
				
			||||||
 | 
						Name                 string                 `json:"name,omitempty"`
 | 
				
			||||||
 | 
						Topic                string                 `json:"topic,omitempty"`
 | 
				
			||||||
 | 
						NSFW                 bool                   `json:"nsfw,omitempty"`
 | 
				
			||||||
 | 
						Position             int                    `json:"position"`
 | 
				
			||||||
 | 
						Bitrate              int                    `json:"bitrate,omitempty"`
 | 
				
			||||||
 | 
						UserLimit            int                    `json:"user_limit,omitempty"`
 | 
				
			||||||
 | 
						PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
 | 
				
			||||||
 | 
						ParentID             string                 `json:"parent_id,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A PermissionOverwrite holds permission overwrite data for a Channel
 | 
					// A PermissionOverwrite holds permission overwrite data for a Channel
 | 
				
			||||||
@@ -191,6 +203,7 @@ type Emoji struct {
 | 
				
			|||||||
	Roles         []string `json:"roles"`
 | 
						Roles         []string `json:"roles"`
 | 
				
			||||||
	Managed       bool     `json:"managed"`
 | 
						Managed       bool     `json:"managed"`
 | 
				
			||||||
	RequireColons bool     `json:"require_colons"`
 | 
						RequireColons bool     `json:"require_colons"`
 | 
				
			||||||
 | 
						Animated      bool     `json:"animated"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
 | 
					// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
 | 
				
			||||||
@@ -204,7 +217,7 @@ func (e *Emoji) APIName() string {
 | 
				
			|||||||
	return e.ID
 | 
						return e.ID
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VerificationLevel type defination
 | 
					// VerificationLevel type definition
 | 
				
			||||||
type VerificationLevel int
 | 
					type VerificationLevel int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Constants for VerificationLevel levels from 0 to 3 inclusive
 | 
					// Constants for VerificationLevel levels from 0 to 3 inclusive
 | 
				
			||||||
@@ -314,45 +327,58 @@ type Presence struct {
 | 
				
			|||||||
	Since  *int     `json:"since"`
 | 
						Since  *int     `json:"since"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GameType is the type of "game" (see GameType* consts) in the Game struct
 | 
				
			||||||
 | 
					type GameType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Valid GameType values
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						GameTypeGame GameType = iota
 | 
				
			||||||
 | 
						GameTypeStreaming
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A Game struct holds the name of the "playing .." game for a user
 | 
					// A Game struct holds the name of the "playing .." game for a user
 | 
				
			||||||
type Game struct {
 | 
					type Game struct {
 | 
				
			||||||
	Name string `json:"name"`
 | 
						Name          string     `json:"name"`
 | 
				
			||||||
	Type int    `json:"type"`
 | 
						Type          GameType   `json:"type"`
 | 
				
			||||||
	URL  string `json:"url,omitempty"`
 | 
						URL           string     `json:"url,omitempty"`
 | 
				
			||||||
 | 
						Details       string     `json:"details,omitempty"`
 | 
				
			||||||
 | 
						State         string     `json:"state,omitempty"`
 | 
				
			||||||
 | 
						TimeStamps    TimeStamps `json:"timestamps,omitempty"`
 | 
				
			||||||
 | 
						Assets        Assets     `json:"assets,omitempty"`
 | 
				
			||||||
 | 
						ApplicationID string     `json:"application_id,omitempty"`
 | 
				
			||||||
 | 
						Instance      int8       `json:"instance,omitempty"`
 | 
				
			||||||
 | 
						// TODO: Party and Secrets (unknown structure)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UnmarshalJSON unmarshals json to Game struct
 | 
					// A TimeStamps struct contains start and end times used in the rich presence "playing .." Game
 | 
				
			||||||
func (g *Game) UnmarshalJSON(bytes []byte) error {
 | 
					type TimeStamps struct {
 | 
				
			||||||
	temp := &struct {
 | 
						EndTimestamp   int64 `json:"end,omitempty"`
 | 
				
			||||||
		Name json.Number     `json:"name"`
 | 
						StartTimestamp int64 `json:"start,omitempty"`
 | 
				
			||||||
		Type json.RawMessage `json:"type"`
 | 
					}
 | 
				
			||||||
		URL  string          `json:"url"`
 | 
					
 | 
				
			||||||
 | 
					// UnmarshalJSON unmarshals JSON into TimeStamps struct
 | 
				
			||||||
 | 
					func (t *TimeStamps) UnmarshalJSON(b []byte) error {
 | 
				
			||||||
 | 
						temp := struct {
 | 
				
			||||||
 | 
							End   float64 `json:"end,omitempty"`
 | 
				
			||||||
 | 
							Start float64 `json:"start,omitempty"`
 | 
				
			||||||
	}{}
 | 
						}{}
 | 
				
			||||||
	err := json.Unmarshal(bytes, temp)
 | 
						err := json.Unmarshal(b, &temp)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	g.URL = temp.URL
 | 
						t.EndTimestamp = int64(temp.End)
 | 
				
			||||||
	g.Name = temp.Name.String()
 | 
						t.StartTimestamp = int64(temp.Start)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if temp.Type != nil {
 | 
					 | 
				
			||||||
		err = json.Unmarshal(temp.Type, &g.Type)
 | 
					 | 
				
			||||||
		if err == nil {
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		s := ""
 | 
					 | 
				
			||||||
		err = json.Unmarshal(temp.Type, &s)
 | 
					 | 
				
			||||||
		if err == nil {
 | 
					 | 
				
			||||||
			g.Type, err = strconv.Atoi(s)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// An Assets struct contains assets and labels used in the rich presence "playing .." Game
 | 
				
			||||||
 | 
					type Assets struct {
 | 
				
			||||||
 | 
						LargeImageID string `json:"large_image,omitempty"`
 | 
				
			||||||
 | 
						SmallImageID string `json:"small_image,omitempty"`
 | 
				
			||||||
 | 
						LargeText    string `json:"large_text,omitempty"`
 | 
				
			||||||
 | 
						SmallText    string `json:"small_text,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A Member stores user information for Guild members.
 | 
					// A Member stores user information for Guild members.
 | 
				
			||||||
type Member struct {
 | 
					type Member struct {
 | 
				
			||||||
	GuildID  string   `json:"guild_id"`
 | 
						GuildID  string   `json:"guild_id"`
 | 
				
			||||||
@@ -383,7 +409,7 @@ type Settings struct {
 | 
				
			|||||||
	DeveloperMode          bool               `json:"developer_mode"`
 | 
						DeveloperMode          bool               `json:"developer_mode"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Status type defination
 | 
					// Status type definition
 | 
				
			||||||
type Status string
 | 
					type Status string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Constants for Status with the different current available status
 | 
					// Constants for Status with the different current available status
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -29,7 +29,9 @@ func (u *User) Mention() string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AvatarURL returns a URL to the user's avatar.
 | 
					// AvatarURL returns a URL to the user's avatar.
 | 
				
			||||||
//		size:     The size of the user's avatar as a power of two
 | 
					//    size:    The size of the user's avatar as a power of two
 | 
				
			||||||
 | 
					//             if size is an empty string, no size parameter will
 | 
				
			||||||
 | 
					//             be added to the URL.
 | 
				
			||||||
func (u *User) AvatarURL(size string) string {
 | 
					func (u *User) AvatarURL(size string) string {
 | 
				
			||||||
	var URL string
 | 
						var URL string
 | 
				
			||||||
	if strings.HasPrefix(u.Avatar, "a_") {
 | 
						if strings.HasPrefix(u.Avatar, "a_") {
 | 
				
			||||||
@@ -38,5 +40,8 @@ func (u *User) AvatarURL(size string) string {
 | 
				
			|||||||
		URL = EndpointUserAvatar(u.ID, u.Avatar)
 | 
							URL = EndpointUserAvatar(u.ID, u.Avatar)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return URL + "?size=" + size
 | 
						if size != "" {
 | 
				
			||||||
 | 
							return URL + "?size=" + size
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return URL
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								vendor/github.com/bwmarrin/discordgo/voice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/bwmarrin/discordgo/voice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,7 +13,6 @@ import (
 | 
				
			|||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
@@ -69,7 +68,7 @@ type VoiceConnection struct {
 | 
				
			|||||||
	voiceSpeakingUpdateHandlers []VoiceSpeakingUpdateHandler
 | 
						voiceSpeakingUpdateHandlers []VoiceSpeakingUpdateHandler
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VoiceSpeakingUpdateHandler type provides a function defination for the
 | 
					// VoiceSpeakingUpdateHandler type provides a function definition for the
 | 
				
			||||||
// VoiceSpeakingUpdate event
 | 
					// VoiceSpeakingUpdate event
 | 
				
			||||||
type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdate)
 | 
					type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -104,7 +103,7 @@ func (v *VoiceConnection) Speaking(b bool) (err error) {
 | 
				
			|||||||
	defer v.Unlock()
 | 
						defer v.Unlock()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		v.speaking = false
 | 
							v.speaking = false
 | 
				
			||||||
		log.Println("Speaking() write json error:", err)
 | 
							v.log(LogError, "Speaking() write json error:", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -181,7 +180,7 @@ func (v *VoiceConnection) Close() {
 | 
				
			|||||||
		v.log(LogInformational, "closing udp")
 | 
							v.log(LogInformational, "closing udp")
 | 
				
			||||||
		err := v.udpConn.Close()
 | 
							err := v.udpConn.Close()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Println("error closing udp connection: ", err)
 | 
								v.log(LogError, "error closing udp connection: ", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		v.udpConn = nil
 | 
							v.udpConn = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -247,7 +246,7 @@ type voiceOP2 struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WaitUntilConnected waits for the Voice Connection to
 | 
					// WaitUntilConnected waits for the Voice Connection to
 | 
				
			||||||
// become ready, if it does not become ready it retuns an err
 | 
					// become ready, if it does not become ready it returns an err
 | 
				
			||||||
func (v *VoiceConnection) waitUntilConnected() error {
 | 
					func (v *VoiceConnection) waitUntilConnected() error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v.log(LogInformational, "called")
 | 
						v.log(LogInformational, "called")
 | 
				
			||||||
@@ -858,7 +857,7 @@ func (v *VoiceConnection) reconnect() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if v.session.DataReady == false || v.session.wsConn == nil {
 | 
							if v.session.DataReady == false || v.session.wsConn == nil {
 | 
				
			||||||
			v.log(LogInformational, "cannot reconenct to channel %s with unready session", v.ChannelID)
 | 
								v.log(LogInformational, "cannot reconnect to channel %s with unready session", v.ChannelID)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										255
									
								
								vendor/github.com/bwmarrin/discordgo/wsapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										255
									
								
								vendor/github.com/bwmarrin/discordgo/wsapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,6 +15,7 @@ import (
 | 
				
			|||||||
	"compress/zlib"
 | 
						"compress/zlib"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
@@ -45,19 +46,114 @@ type resumePacket struct {
 | 
				
			|||||||
	} `json:"d"`
 | 
						} `json:"d"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Open opens a websocket connection to Discord.
 | 
					// Open creates a websocket connection to Discord.
 | 
				
			||||||
func (s *Session) Open() (err error) {
 | 
					// See: https://discordapp.com/developers/docs/topics/gateway#connecting
 | 
				
			||||||
 | 
					func (s *Session) Open() error {
 | 
				
			||||||
	s.log(LogInformational, "called")
 | 
						s.log(LogInformational, "called")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Prevent Open or other major Session functions from
 | 
				
			||||||
 | 
						// being called while Open is still running.
 | 
				
			||||||
	s.Lock()
 | 
						s.Lock()
 | 
				
			||||||
	defer func() {
 | 
						defer s.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the websock is already open, bail out here.
 | 
				
			||||||
 | 
						if s.wsConn != nil {
 | 
				
			||||||
 | 
							return ErrWSAlreadyOpen
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get the gateway to use for the Websocket connection
 | 
				
			||||||
 | 
						if s.gateway == "" {
 | 
				
			||||||
 | 
							s.gateway, err = s.Gateway()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			s.Unlock()
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Add the version and encoding to the URL
 | 
				
			||||||
 | 
							s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Connect to the Gateway
 | 
				
			||||||
 | 
						s.log(LogInformational, "connecting to gateway %s", s.gateway)
 | 
				
			||||||
 | 
						header := http.Header{}
 | 
				
			||||||
 | 
						header.Add("accept-encoding", "zlib")
 | 
				
			||||||
 | 
						s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
 | 
				
			||||||
 | 
							s.gateway = "" // clear cached gateway
 | 
				
			||||||
 | 
							s.wsConn = nil // Just to be safe.
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							// because of this, all code below must set err to the error
 | 
				
			||||||
 | 
							// when exiting with an error :)  Maybe someone has a better
 | 
				
			||||||
 | 
							// way :)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								s.wsConn.Close()
 | 
				
			||||||
 | 
								s.wsConn = nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The first response from Discord should be an Op 10 (Hello) Packet.
 | 
				
			||||||
 | 
						// When processed by onEvent the heartbeat goroutine will be started.
 | 
				
			||||||
 | 
						mt, m, err := s.wsConn.ReadMessage()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e, err := s.onEvent(mt, m)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if e.Operation != 10 {
 | 
				
			||||||
 | 
							err = fmt.Errorf("expecting Op 10, got Op %d instead", e.Operation)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s.log(LogInformational, "Op 10 Hello Packet received from Discord")
 | 
				
			||||||
 | 
						s.LastHeartbeatAck = time.Now().UTC()
 | 
				
			||||||
 | 
						var h helloOp
 | 
				
			||||||
 | 
						if err = json.Unmarshal(e.RawData, &h); err != nil {
 | 
				
			||||||
 | 
							err = fmt.Errorf("error unmarshalling helloOp, %s", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now we send either an Op 2 Identity if this is a brand new
 | 
				
			||||||
 | 
						// connection or Op 6 Resume if we are resuming an existing connection.
 | 
				
			||||||
 | 
						sequence := atomic.LoadInt64(s.sequence)
 | 
				
			||||||
 | 
						if s.sessionID == "" && sequence == 0 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Send Op 2 Identity Packet
 | 
				
			||||||
 | 
							err = s.identify()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								err = fmt.Errorf("error sending identify packet to gateway, %s, %s", s.gateway, err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Send Op 6 Resume Packet
 | 
				
			||||||
 | 
							p := resumePacket{}
 | 
				
			||||||
 | 
							p.Op = 6
 | 
				
			||||||
 | 
							p.Data.Token = s.Token
 | 
				
			||||||
 | 
							p.Data.SessionID = s.sessionID
 | 
				
			||||||
 | 
							p.Data.Sequence = sequence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							s.log(LogInformational, "sending resume packet to gateway")
 | 
				
			||||||
 | 
							s.wsMutex.Lock()
 | 
				
			||||||
 | 
							err = s.wsConn.WriteJSON(p)
 | 
				
			||||||
 | 
							s.wsMutex.Unlock()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								err = fmt.Errorf("error sending gateway resume packet, %s, %s", s.gateway, err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// A basic state is a hard requirement for Voice.
 | 
						// A basic state is a hard requirement for Voice.
 | 
				
			||||||
 | 
						// We create it here so the below READY/RESUMED packet can populate
 | 
				
			||||||
 | 
						// the state :)
 | 
				
			||||||
 | 
						// XXX: Move to New() func?
 | 
				
			||||||
	if s.State == nil {
 | 
						if s.State == nil {
 | 
				
			||||||
		state := NewState()
 | 
							state := NewState()
 | 
				
			||||||
		state.TrackChannels = false
 | 
							state.TrackChannels = false
 | 
				
			||||||
@@ -68,77 +164,42 @@ func (s *Session) Open() (err error) {
 | 
				
			|||||||
		s.State = state
 | 
							s.State = state
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if s.wsConn != nil {
 | 
						// Now Discord should send us a READY or RESUMED packet.
 | 
				
			||||||
		err = ErrWSAlreadyOpen
 | 
						mt, m, err = s.wsConn.ReadMessage()
 | 
				
			||||||
		return
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						e, err = s.onEvent(mt, m)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if e.Type != `READY` && e.Type != `RESUMED` {
 | 
				
			||||||
 | 
							// This is not fatal, but it does not follow their API documentation.
 | 
				
			||||||
 | 
							s.log(LogWarning, "Expected READY/RESUMED, instead got:\n%#v\n", e)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s.log(LogInformational, "First Packet:\n%#v\n", e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.log(LogInformational, "We are now connected to Discord, emitting connect event")
 | 
				
			||||||
 | 
						s.handleEvent(connectEventType, &Connect{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// A VoiceConnections map is a hard requirement for Voice.
 | 
				
			||||||
 | 
						// XXX: can this be moved to when opening a voice connection?
 | 
				
			||||||
	if s.VoiceConnections == nil {
 | 
						if s.VoiceConnections == nil {
 | 
				
			||||||
		s.log(LogInformational, "creating new VoiceConnections map")
 | 
							s.log(LogInformational, "creating new VoiceConnections map")
 | 
				
			||||||
		s.VoiceConnections = make(map[string]*VoiceConnection)
 | 
							s.VoiceConnections = make(map[string]*VoiceConnection)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get the gateway to use for the Websocket connection
 | 
						// Create listening chan outside of listen, as it needs to happen inside the
 | 
				
			||||||
	if s.gateway == "" {
 | 
						// mutex lock and needs to exist before calling heartbeat and listen
 | 
				
			||||||
		s.gateway, err = s.Gateway()
 | 
						// go rountines.
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Add the version and encoding to the URL
 | 
					 | 
				
			||||||
		s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	header := http.Header{}
 | 
					 | 
				
			||||||
	header.Add("accept-encoding", "zlib")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.log(LogInformational, "connecting to gateway %s", s.gateway)
 | 
					 | 
				
			||||||
	s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
 | 
					 | 
				
			||||||
		s.gateway = "" // clear cached gateway
 | 
					 | 
				
			||||||
		// TODO: should we add a retry block here?
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sequence := atomic.LoadInt64(s.sequence)
 | 
					 | 
				
			||||||
	if s.sessionID != "" && sequence > 0 {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p := resumePacket{}
 | 
					 | 
				
			||||||
		p.Op = 6
 | 
					 | 
				
			||||||
		p.Data.Token = s.Token
 | 
					 | 
				
			||||||
		p.Data.SessionID = s.sessionID
 | 
					 | 
				
			||||||
		p.Data.Sequence = sequence
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		s.log(LogInformational, "sending resume packet to gateway")
 | 
					 | 
				
			||||||
		err = s.wsConn.WriteJSON(p)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			s.log(LogWarning, "error sending gateway resume packet, %s, %s", s.gateway, err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err = s.identify()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create listening outside of listen, as it needs to happen inside the mutex
 | 
					 | 
				
			||||||
	// lock.
 | 
					 | 
				
			||||||
	s.listening = make(chan interface{})
 | 
						s.listening = make(chan interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start sending heartbeats and reading messages from Discord.
 | 
				
			||||||
 | 
						go s.heartbeat(s.wsConn, s.listening, h.HeartbeatInterval)
 | 
				
			||||||
	go s.listen(s.wsConn, s.listening)
 | 
						go s.listen(s.wsConn, s.listening)
 | 
				
			||||||
	s.LastHeartbeatAck = time.Now().UTC()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.log(LogInformational, "emit connect event")
 | 
					 | 
				
			||||||
	s.handleEvent(connectEventType, &Connect{})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.log(LogInformational, "exiting")
 | 
						s.log(LogInformational, "exiting")
 | 
				
			||||||
	return
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// listen polls the websocket connection for events, it will stop when the
 | 
					// listen polls the websocket connection for events, it will stop when the
 | 
				
			||||||
@@ -249,7 +310,8 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type updateStatusData struct {
 | 
					// UpdateStatusData ia provided to UpdateStatusComplex()
 | 
				
			||||||
 | 
					type UpdateStatusData struct {
 | 
				
			||||||
	IdleSince *int   `json:"since"`
 | 
						IdleSince *int   `json:"since"`
 | 
				
			||||||
	Game      *Game  `json:"game"`
 | 
						Game      *Game  `json:"game"`
 | 
				
			||||||
	AFK       bool   `json:"afk"`
 | 
						AFK       bool   `json:"afk"`
 | 
				
			||||||
@@ -258,7 +320,7 @@ type updateStatusData struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type updateStatusOp struct {
 | 
					type updateStatusOp struct {
 | 
				
			||||||
	Op   int              `json:"op"`
 | 
						Op   int              `json:"op"`
 | 
				
			||||||
	Data updateStatusData `json:"d"`
 | 
						Data UpdateStatusData `json:"d"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateStreamingStatus is used to update the user's streaming status.
 | 
					// UpdateStreamingStatus is used to update the user's streaming status.
 | 
				
			||||||
@@ -270,13 +332,7 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	s.log(LogInformational, "called")
 | 
						s.log(LogInformational, "called")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.RLock()
 | 
						usd := UpdateStatusData{
 | 
				
			||||||
	defer s.RUnlock()
 | 
					 | 
				
			||||||
	if s.wsConn == nil {
 | 
					 | 
				
			||||||
		return ErrWSNotFound
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	usd := updateStatusData{
 | 
					 | 
				
			||||||
		Status: "online",
 | 
							Status: "online",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -285,9 +341,9 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if game != "" {
 | 
						if game != "" {
 | 
				
			||||||
		gameType := 0
 | 
							gameType := GameTypeGame
 | 
				
			||||||
		if url != "" {
 | 
							if url != "" {
 | 
				
			||||||
			gameType = 1
 | 
								gameType = GameTypeStreaming
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		usd.Game = &Game{
 | 
							usd.Game = &Game{
 | 
				
			||||||
			Name: game,
 | 
								Name: game,
 | 
				
			||||||
@@ -296,6 +352,18 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return s.UpdateStatusComplex(usd)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateStatusComplex allows for sending the raw status update data untouched by discordgo.
 | 
				
			||||||
 | 
					func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.RLock()
 | 
				
			||||||
 | 
						defer s.RUnlock()
 | 
				
			||||||
 | 
						if s.wsConn == nil {
 | 
				
			||||||
 | 
							return ErrWSNotFound
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.wsMutex.Lock()
 | 
						s.wsMutex.Lock()
 | 
				
			||||||
	err = s.wsConn.WriteJSON(updateStatusOp{3, usd})
 | 
						err = s.wsConn.WriteJSON(updateStatusOp{3, usd})
 | 
				
			||||||
	s.wsMutex.Unlock()
 | 
						s.wsMutex.Unlock()
 | 
				
			||||||
@@ -357,9 +425,7 @@ func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err err
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// If you use the AddHandler() function to register a handler for the
 | 
					// If you use the AddHandler() function to register a handler for the
 | 
				
			||||||
// "OnEvent" event then all events will be passed to that handler.
 | 
					// "OnEvent" event then all events will be passed to that handler.
 | 
				
			||||||
//
 | 
					func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
				
			||||||
// TODO: You may also register a custom event handler entirely using...
 | 
					 | 
				
			||||||
func (s *Session) onEvent(messageType int, message []byte) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var reader io.Reader
 | 
						var reader io.Reader
 | 
				
			||||||
@@ -371,7 +437,7 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
		z, err2 := zlib.NewReader(reader)
 | 
							z, err2 := zlib.NewReader(reader)
 | 
				
			||||||
		if err2 != nil {
 | 
							if err2 != nil {
 | 
				
			||||||
			s.log(LogError, "error uncompressing websocket message, %s", err)
 | 
								s.log(LogError, "error uncompressing websocket message, %s", err)
 | 
				
			||||||
			return
 | 
								return nil, err2
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		defer func() {
 | 
							defer func() {
 | 
				
			||||||
@@ -389,7 +455,7 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
	decoder := json.NewDecoder(reader)
 | 
						decoder := json.NewDecoder(reader)
 | 
				
			||||||
	if err = decoder.Decode(&e); err != nil {
 | 
						if err = decoder.Decode(&e); err != nil {
 | 
				
			||||||
		s.log(LogError, "error decoding websocket message, %s", err)
 | 
							s.log(LogError, "error decoding websocket message, %s", err)
 | 
				
			||||||
		return
 | 
							return e, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.log(LogDebug, "Op: %d, Seq: %d, Type: %s, Data: %s\n\n", e.Operation, e.Sequence, e.Type, string(e.RawData))
 | 
						s.log(LogDebug, "Op: %d, Seq: %d, Type: %s, Data: %s\n\n", e.Operation, e.Sequence, e.Type, string(e.RawData))
 | 
				
			||||||
@@ -403,10 +469,10 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
		s.wsMutex.Unlock()
 | 
							s.wsMutex.Unlock()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			s.log(LogError, "error sending heartbeat in response to Op1")
 | 
								s.log(LogError, "error sending heartbeat in response to Op1")
 | 
				
			||||||
			return
 | 
								return e, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return
 | 
							return e, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Reconnect
 | 
						// Reconnect
 | 
				
			||||||
@@ -415,7 +481,7 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
		s.log(LogInformational, "Closing and reconnecting in response to Op7")
 | 
							s.log(LogInformational, "Closing and reconnecting in response to Op7")
 | 
				
			||||||
		s.Close()
 | 
							s.Close()
 | 
				
			||||||
		s.reconnect()
 | 
							s.reconnect()
 | 
				
			||||||
		return
 | 
							return e, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Invalid Session
 | 
						// Invalid Session
 | 
				
			||||||
@@ -427,20 +493,15 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
		err = s.identify()
 | 
							err = s.identify()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
 | 
								s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
 | 
				
			||||||
			return
 | 
								return e, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return
 | 
							return e, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if e.Operation == 10 {
 | 
						if e.Operation == 10 {
 | 
				
			||||||
		var h helloOp
 | 
							// Op10 is handled by Open()
 | 
				
			||||||
		if err = json.Unmarshal(e.RawData, &h); err != nil {
 | 
							return e, nil
 | 
				
			||||||
			s.log(LogError, "error unmarshalling helloOp, %s", err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			go s.heartbeat(s.wsConn, s.listening, h.HeartbeatInterval)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if e.Operation == 11 {
 | 
						if e.Operation == 11 {
 | 
				
			||||||
@@ -448,7 +509,7 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
		s.LastHeartbeatAck = time.Now().UTC()
 | 
							s.LastHeartbeatAck = time.Now().UTC()
 | 
				
			||||||
		s.Unlock()
 | 
							s.Unlock()
 | 
				
			||||||
		s.log(LogInformational, "got heartbeat ACK")
 | 
							s.log(LogInformational, "got heartbeat ACK")
 | 
				
			||||||
		return
 | 
							return e, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Do not try to Dispatch a non-Dispatch Message
 | 
						// Do not try to Dispatch a non-Dispatch Message
 | 
				
			||||||
@@ -456,7 +517,7 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
		// But we probably should be doing something with them.
 | 
							// But we probably should be doing something with them.
 | 
				
			||||||
		// TEMP
 | 
							// TEMP
 | 
				
			||||||
		s.log(LogWarning, "unknown Op: %d, Seq: %d, Type: %s, Data: %s, message: %s", e.Operation, e.Sequence, e.Type, string(e.RawData), string(message))
 | 
							s.log(LogWarning, "unknown Op: %d, Seq: %d, Type: %s, Data: %s, message: %s", e.Operation, e.Sequence, e.Type, string(e.RawData), string(message))
 | 
				
			||||||
		return
 | 
							return e, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Store the message sequence
 | 
						// Store the message sequence
 | 
				
			||||||
@@ -485,6 +546,8 @@ func (s *Session) onEvent(messageType int, message []byte) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
 | 
						// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
 | 
				
			||||||
	s.handleEvent(eventEventType, e)
 | 
						s.handleEvent(eventEventType, e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return e, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ------------------------------------------------------------------------------------------------
 | 
					// ------------------------------------------------------------------------------------------------
 | 
				
			||||||
@@ -610,7 +673,7 @@ func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
 | 
				
			|||||||
	voice.GuildID = st.GuildID
 | 
						voice.GuildID = st.GuildID
 | 
				
			||||||
	voice.Unlock()
 | 
						voice.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Open a conenction to the voice server
 | 
						// Open a connection to the voice server
 | 
				
			||||||
	err := voice.open()
 | 
						err := voice.open()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
 | 
							s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										202
									
								
								vendor/github.com/coreos/etcd/client/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/coreos/etcd/client/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   END OF TERMS AND CONDITIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   APPENDIX: How to apply the Apache License to your work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To apply the Apache License to your work, attach the following
 | 
				
			||||||
 | 
					      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
				
			||||||
 | 
					      replaced with your own identifying information. (Don't include
 | 
				
			||||||
 | 
					      the brackets!)  The text should be enclosed in the appropriate
 | 
				
			||||||
 | 
					      comment syntax for the file format. We also recommend that a
 | 
				
			||||||
 | 
					      file or class name and description of purpose be included on the
 | 
				
			||||||
 | 
					      same "printed page" as the copyright notice for easier
 | 
				
			||||||
 | 
					      identification within third-party archives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright [yyyy] [name of copyright owner]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										236
									
								
								vendor/github.com/coreos/etcd/client/auth_role.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								vendor/github.com/coreos/etcd/client/auth_role.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Role struct {
 | 
				
			||||||
 | 
						Role        string       `json:"role"`
 | 
				
			||||||
 | 
						Permissions Permissions  `json:"permissions"`
 | 
				
			||||||
 | 
						Grant       *Permissions `json:"grant,omitempty"`
 | 
				
			||||||
 | 
						Revoke      *Permissions `json:"revoke,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Permissions struct {
 | 
				
			||||||
 | 
						KV rwPermission `json:"kv"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type rwPermission struct {
 | 
				
			||||||
 | 
						Read  []string `json:"read"`
 | 
				
			||||||
 | 
						Write []string `json:"write"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PermissionType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ReadPermission PermissionType = iota
 | 
				
			||||||
 | 
						WritePermission
 | 
				
			||||||
 | 
						ReadWritePermission
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to
 | 
				
			||||||
 | 
					// interact with etcd's role creation and modification features.
 | 
				
			||||||
 | 
					func NewAuthRoleAPI(c Client) AuthRoleAPI {
 | 
				
			||||||
 | 
						return &httpAuthRoleAPI{
 | 
				
			||||||
 | 
							client: c,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AuthRoleAPI interface {
 | 
				
			||||||
 | 
						// AddRole adds a role.
 | 
				
			||||||
 | 
						AddRole(ctx context.Context, role string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RemoveRole removes a role.
 | 
				
			||||||
 | 
						RemoveRole(ctx context.Context, role string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetRole retrieves role details.
 | 
				
			||||||
 | 
						GetRole(ctx context.Context, role string) (*Role, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GrantRoleKV grants a role some permission prefixes for the KV store.
 | 
				
			||||||
 | 
						GrantRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RevokeRoleKV revokes some permission prefixes for a role on the KV store.
 | 
				
			||||||
 | 
						RevokeRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListRoles lists roles.
 | 
				
			||||||
 | 
						ListRoles(ctx context.Context) ([]string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpAuthRoleAPI struct {
 | 
				
			||||||
 | 
						client httpClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authRoleAPIAction struct {
 | 
				
			||||||
 | 
						verb string
 | 
				
			||||||
 | 
						name string
 | 
				
			||||||
 | 
						role *Role
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authRoleAPIList struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2AuthURL(ep, "roles", "")
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("GET", u.String(), nil)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2AuthURL(ep, "roles", l.name)
 | 
				
			||||||
 | 
						if l.role == nil {
 | 
				
			||||||
 | 
							req, _ := http.NewRequest(l.verb, u.String(), nil)
 | 
				
			||||||
 | 
							return req
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b, err := json.Marshal(l.role)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						body := bytes.NewReader(b)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest(l.verb, u.String(), body)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
 | 
				
			||||||
 | 
						resp, body, err := r.client.Do(ctx, &authRoleAPIList{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var roleList struct {
 | 
				
			||||||
 | 
							Roles []Role `json:"roles"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = json.Unmarshal(body, &roleList); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret := make([]string, 0, len(roleList.Roles))
 | 
				
			||||||
 | 
						for _, r := range roleList.Roles {
 | 
				
			||||||
 | 
							ret = append(ret, r.Role)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error {
 | 
				
			||||||
 | 
						role := &Role{
 | 
				
			||||||
 | 
							Role: rolename,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r.addRemoveRole(ctx, &authRoleAPIAction{
 | 
				
			||||||
 | 
							verb: "PUT",
 | 
				
			||||||
 | 
							name: rolename,
 | 
				
			||||||
 | 
							role: role,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
 | 
				
			||||||
 | 
						return r.addRemoveRole(ctx, &authRoleAPIAction{
 | 
				
			||||||
 | 
							verb: "DELETE",
 | 
				
			||||||
 | 
							name: rolename,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error {
 | 
				
			||||||
 | 
						resp, body, err := r.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
 | 
				
			||||||
 | 
							var sec authError
 | 
				
			||||||
 | 
							err := json.Unmarshal(body, &sec)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return sec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
 | 
				
			||||||
 | 
						return r.modRole(ctx, &authRoleAPIAction{
 | 
				
			||||||
 | 
							verb: "GET",
 | 
				
			||||||
 | 
							name: rolename,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func buildRWPermission(prefixes []string, permType PermissionType) rwPermission {
 | 
				
			||||||
 | 
						var out rwPermission
 | 
				
			||||||
 | 
						switch permType {
 | 
				
			||||||
 | 
						case ReadPermission:
 | 
				
			||||||
 | 
							out.Read = prefixes
 | 
				
			||||||
 | 
						case WritePermission:
 | 
				
			||||||
 | 
							out.Write = prefixes
 | 
				
			||||||
 | 
						case ReadWritePermission:
 | 
				
			||||||
 | 
							out.Read = prefixes
 | 
				
			||||||
 | 
							out.Write = prefixes
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
 | 
				
			||||||
 | 
						rwp := buildRWPermission(prefixes, permType)
 | 
				
			||||||
 | 
						role := &Role{
 | 
				
			||||||
 | 
							Role: rolename,
 | 
				
			||||||
 | 
							Grant: &Permissions{
 | 
				
			||||||
 | 
								KV: rwp,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r.modRole(ctx, &authRoleAPIAction{
 | 
				
			||||||
 | 
							verb: "PUT",
 | 
				
			||||||
 | 
							name: rolename,
 | 
				
			||||||
 | 
							role: role,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
 | 
				
			||||||
 | 
						rwp := buildRWPermission(prefixes, permType)
 | 
				
			||||||
 | 
						role := &Role{
 | 
				
			||||||
 | 
							Role: rolename,
 | 
				
			||||||
 | 
							Revoke: &Permissions{
 | 
				
			||||||
 | 
								KV: rwp,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r.modRole(ctx, &authRoleAPIAction{
 | 
				
			||||||
 | 
							verb: "PUT",
 | 
				
			||||||
 | 
							name: rolename,
 | 
				
			||||||
 | 
							role: role,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) {
 | 
				
			||||||
 | 
						resp, body, err := r.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
 | 
				
			||||||
 | 
							var sec authError
 | 
				
			||||||
 | 
							err = json.Unmarshal(body, &sec)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, sec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var role Role
 | 
				
			||||||
 | 
						if err = json.Unmarshal(body, &role); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &role, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										319
									
								
								vendor/github.com/coreos/etcd/client/auth_user.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								vendor/github.com/coreos/etcd/client/auth_user.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,319 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						defaultV2AuthPrefix = "/v2/auth"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type User struct {
 | 
				
			||||||
 | 
						User     string   `json:"user"`
 | 
				
			||||||
 | 
						Password string   `json:"password,omitempty"`
 | 
				
			||||||
 | 
						Roles    []string `json:"roles"`
 | 
				
			||||||
 | 
						Grant    []string `json:"grant,omitempty"`
 | 
				
			||||||
 | 
						Revoke   []string `json:"revoke,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// userListEntry is the user representation given by the server for ListUsers
 | 
				
			||||||
 | 
					type userListEntry struct {
 | 
				
			||||||
 | 
						User  string `json:"user"`
 | 
				
			||||||
 | 
						Roles []Role `json:"roles"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UserRoles struct {
 | 
				
			||||||
 | 
						User  string `json:"user"`
 | 
				
			||||||
 | 
						Roles []Role `json:"roles"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func v2AuthURL(ep url.URL, action string, name string) *url.URL {
 | 
				
			||||||
 | 
						if name != "" {
 | 
				
			||||||
 | 
							ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action, name)
 | 
				
			||||||
 | 
							return &ep
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action)
 | 
				
			||||||
 | 
						return &ep
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewAuthAPI constructs a new AuthAPI that uses HTTP to
 | 
				
			||||||
 | 
					// interact with etcd's general auth features.
 | 
				
			||||||
 | 
					func NewAuthAPI(c Client) AuthAPI {
 | 
				
			||||||
 | 
						return &httpAuthAPI{
 | 
				
			||||||
 | 
							client: c,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AuthAPI interface {
 | 
				
			||||||
 | 
						// Enable auth.
 | 
				
			||||||
 | 
						Enable(ctx context.Context) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Disable auth.
 | 
				
			||||||
 | 
						Disable(ctx context.Context) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpAuthAPI struct {
 | 
				
			||||||
 | 
						client httpClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *httpAuthAPI) Enable(ctx context.Context) error {
 | 
				
			||||||
 | 
						return s.enableDisable(ctx, &authAPIAction{"PUT"})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *httpAuthAPI) Disable(ctx context.Context) error {
 | 
				
			||||||
 | 
						return s.enableDisable(ctx, &authAPIAction{"DELETE"})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *httpAuthAPI) enableDisable(ctx context.Context, req httpAction) error {
 | 
				
			||||||
 | 
						resp, body, err := s.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
 | 
				
			||||||
 | 
							var sec authError
 | 
				
			||||||
 | 
							err = json.Unmarshal(body, &sec)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return sec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authAPIAction struct {
 | 
				
			||||||
 | 
						verb string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *authAPIAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2AuthURL(ep, "enable", "")
 | 
				
			||||||
 | 
						req, _ := http.NewRequest(l.verb, u.String(), nil)
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authError struct {
 | 
				
			||||||
 | 
						Message string `json:"message"`
 | 
				
			||||||
 | 
						Code    int    `json:"-"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e authError) Error() string {
 | 
				
			||||||
 | 
						return e.Message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewAuthUserAPI constructs a new AuthUserAPI that uses HTTP to
 | 
				
			||||||
 | 
					// interact with etcd's user creation and modification features.
 | 
				
			||||||
 | 
					func NewAuthUserAPI(c Client) AuthUserAPI {
 | 
				
			||||||
 | 
						return &httpAuthUserAPI{
 | 
				
			||||||
 | 
							client: c,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AuthUserAPI interface {
 | 
				
			||||||
 | 
						// AddUser adds a user.
 | 
				
			||||||
 | 
						AddUser(ctx context.Context, username string, password string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RemoveUser removes a user.
 | 
				
			||||||
 | 
						RemoveUser(ctx context.Context, username string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetUser retrieves user details.
 | 
				
			||||||
 | 
						GetUser(ctx context.Context, username string) (*User, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GrantUser grants a user some permission roles.
 | 
				
			||||||
 | 
						GrantUser(ctx context.Context, username string, roles []string) (*User, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RevokeUser revokes some permission roles from a user.
 | 
				
			||||||
 | 
						RevokeUser(ctx context.Context, username string, roles []string) (*User, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ChangePassword changes the user's password.
 | 
				
			||||||
 | 
						ChangePassword(ctx context.Context, username string, password string) (*User, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListUsers lists the users.
 | 
				
			||||||
 | 
						ListUsers(ctx context.Context) ([]string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpAuthUserAPI struct {
 | 
				
			||||||
 | 
						client httpClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authUserAPIAction struct {
 | 
				
			||||||
 | 
						verb     string
 | 
				
			||||||
 | 
						username string
 | 
				
			||||||
 | 
						user     *User
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authUserAPIList struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (list *authUserAPIList) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2AuthURL(ep, "users", "")
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("GET", u.String(), nil)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *authUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2AuthURL(ep, "users", l.username)
 | 
				
			||||||
 | 
						if l.user == nil {
 | 
				
			||||||
 | 
							req, _ := http.NewRequest(l.verb, u.String(), nil)
 | 
				
			||||||
 | 
							return req
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b, err := json.Marshal(l.user)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						body := bytes.NewReader(b)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest(l.verb, u.String(), body)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
 | 
				
			||||||
 | 
						resp, body, err := u.client.Do(ctx, &authUserAPIList{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
 | 
				
			||||||
 | 
							var sec authError
 | 
				
			||||||
 | 
							err = json.Unmarshal(body, &sec)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, sec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var userList struct {
 | 
				
			||||||
 | 
							Users []userListEntry `json:"users"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = json.Unmarshal(body, &userList); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret := make([]string, 0, len(userList.Users))
 | 
				
			||||||
 | 
						for _, u := range userList.Users {
 | 
				
			||||||
 | 
							ret = append(ret, u.User)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) AddUser(ctx context.Context, username string, password string) error {
 | 
				
			||||||
 | 
						user := &User{
 | 
				
			||||||
 | 
							User:     username,
 | 
				
			||||||
 | 
							Password: password,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return u.addRemoveUser(ctx, &authUserAPIAction{
 | 
				
			||||||
 | 
							verb:     "PUT",
 | 
				
			||||||
 | 
							username: username,
 | 
				
			||||||
 | 
							user:     user,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) RemoveUser(ctx context.Context, username string) error {
 | 
				
			||||||
 | 
						return u.addRemoveUser(ctx, &authUserAPIAction{
 | 
				
			||||||
 | 
							verb:     "DELETE",
 | 
				
			||||||
 | 
							username: username,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) addRemoveUser(ctx context.Context, req *authUserAPIAction) error {
 | 
				
			||||||
 | 
						resp, body, err := u.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
 | 
				
			||||||
 | 
							var sec authError
 | 
				
			||||||
 | 
							err = json.Unmarshal(body, &sec)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return sec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
 | 
				
			||||||
 | 
						return u.modUser(ctx, &authUserAPIAction{
 | 
				
			||||||
 | 
							verb:     "GET",
 | 
				
			||||||
 | 
							username: username,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
 | 
				
			||||||
 | 
						user := &User{
 | 
				
			||||||
 | 
							User:  username,
 | 
				
			||||||
 | 
							Grant: roles,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return u.modUser(ctx, &authUserAPIAction{
 | 
				
			||||||
 | 
							verb:     "PUT",
 | 
				
			||||||
 | 
							username: username,
 | 
				
			||||||
 | 
							user:     user,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
 | 
				
			||||||
 | 
						user := &User{
 | 
				
			||||||
 | 
							User:   username,
 | 
				
			||||||
 | 
							Revoke: roles,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return u.modUser(ctx, &authUserAPIAction{
 | 
				
			||||||
 | 
							verb:     "PUT",
 | 
				
			||||||
 | 
							username: username,
 | 
				
			||||||
 | 
							user:     user,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
 | 
				
			||||||
 | 
						user := &User{
 | 
				
			||||||
 | 
							User:     username,
 | 
				
			||||||
 | 
							Password: password,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return u.modUser(ctx, &authUserAPIAction{
 | 
				
			||||||
 | 
							verb:     "PUT",
 | 
				
			||||||
 | 
							username: username,
 | 
				
			||||||
 | 
							user:     user,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *httpAuthUserAPI) modUser(ctx context.Context, req *authUserAPIAction) (*User, error) {
 | 
				
			||||||
 | 
						resp, body, err := u.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
 | 
				
			||||||
 | 
							var sec authError
 | 
				
			||||||
 | 
							err = json.Unmarshal(body, &sec)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, sec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var user User
 | 
				
			||||||
 | 
						if err = json.Unmarshal(body, &user); err != nil {
 | 
				
			||||||
 | 
							var userR UserRoles
 | 
				
			||||||
 | 
							if urerr := json.Unmarshal(body, &userR); urerr != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							user.User = userR.User
 | 
				
			||||||
 | 
							for _, r := range userR.Roles {
 | 
				
			||||||
 | 
								user.Roles = append(user.Roles, r.Role)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &user, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								vendor/github.com/coreos/etcd/client/cancelreq.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/coreos/etcd/client/cancelreq.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The Go Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// borrowed from golang/net/context/ctxhttp/cancelreq.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func requestCanceler(tr CancelableTransport, req *http.Request) func() {
 | 
				
			||||||
 | 
						ch := make(chan struct{})
 | 
				
			||||||
 | 
						req.Cancel = ch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return func() {
 | 
				
			||||||
 | 
							close(ch)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										710
									
								
								vendor/github.com/coreos/etcd/client/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										710
									
								
								vendor/github.com/coreos/etcd/client/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,710 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/coreos/etcd/version"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						ErrNoEndpoints           = errors.New("client: no endpoints available")
 | 
				
			||||||
 | 
						ErrTooManyRedirects      = errors.New("client: too many redirects")
 | 
				
			||||||
 | 
						ErrClusterUnavailable    = errors.New("client: etcd cluster is unavailable or misconfigured")
 | 
				
			||||||
 | 
						ErrNoLeaderEndpoint      = errors.New("client: no leader endpoint available")
 | 
				
			||||||
 | 
						errTooManyRedirectChecks = errors.New("client: too many redirect checks")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// oneShotCtxValue is set on a context using WithValue(&oneShotValue) so
 | 
				
			||||||
 | 
						// that Do() will not retry a request
 | 
				
			||||||
 | 
						oneShotCtxValue interface{}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var DefaultRequestTimeout = 5 * time.Second
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var DefaultTransport CancelableTransport = &http.Transport{
 | 
				
			||||||
 | 
						Proxy: http.ProxyFromEnvironment,
 | 
				
			||||||
 | 
						Dial: (&net.Dialer{
 | 
				
			||||||
 | 
							Timeout:   30 * time.Second,
 | 
				
			||||||
 | 
							KeepAlive: 30 * time.Second,
 | 
				
			||||||
 | 
						}).Dial,
 | 
				
			||||||
 | 
						TLSHandshakeTimeout: 10 * time.Second,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type EndpointSelectionMode int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// EndpointSelectionRandom is the default value of the 'SelectionMode'.
 | 
				
			||||||
 | 
						// As the name implies, the client object will pick a node from the members
 | 
				
			||||||
 | 
						// of the cluster in a random fashion. If the cluster has three members, A, B,
 | 
				
			||||||
 | 
						// and C, the client picks any node from its three members as its request
 | 
				
			||||||
 | 
						// destination.
 | 
				
			||||||
 | 
						EndpointSelectionRandom EndpointSelectionMode = iota
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If 'SelectionMode' is set to 'EndpointSelectionPrioritizeLeader',
 | 
				
			||||||
 | 
						// requests are sent directly to the cluster leader. This reduces
 | 
				
			||||||
 | 
						// forwarding roundtrips compared to making requests to etcd followers
 | 
				
			||||||
 | 
						// who then forward them to the cluster leader. In the event of a leader
 | 
				
			||||||
 | 
						// failure, however, clients configured this way cannot prioritize among
 | 
				
			||||||
 | 
						// the remaining etcd followers. Therefore, when a client sets 'SelectionMode'
 | 
				
			||||||
 | 
						// to 'EndpointSelectionPrioritizeLeader', it must use 'client.AutoSync()' to
 | 
				
			||||||
 | 
						// maintain its knowledge of current cluster state.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// This mode should be used with Client.AutoSync().
 | 
				
			||||||
 | 
						EndpointSelectionPrioritizeLeader
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Config struct {
 | 
				
			||||||
 | 
						// Endpoints defines a set of URLs (schemes, hosts and ports only)
 | 
				
			||||||
 | 
						// that can be used to communicate with a logical etcd cluster. For
 | 
				
			||||||
 | 
						// example, a three-node cluster could be provided like so:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 	Endpoints: []string{
 | 
				
			||||||
 | 
						//		"http://node1.example.com:2379",
 | 
				
			||||||
 | 
						//		"http://node2.example.com:2379",
 | 
				
			||||||
 | 
						//		"http://node3.example.com:2379",
 | 
				
			||||||
 | 
						//	}
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If multiple endpoints are provided, the Client will attempt to
 | 
				
			||||||
 | 
						// use them all in the event that one or more of them are unusable.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If Client.Sync is ever called, the Client may cache an alternate
 | 
				
			||||||
 | 
						// set of endpoints to continue operation.
 | 
				
			||||||
 | 
						Endpoints []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Transport is used by the Client to drive HTTP requests. If not
 | 
				
			||||||
 | 
						// provided, DefaultTransport will be used.
 | 
				
			||||||
 | 
						Transport CancelableTransport
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// CheckRedirect specifies the policy for handling HTTP redirects.
 | 
				
			||||||
 | 
						// If CheckRedirect is not nil, the Client calls it before
 | 
				
			||||||
 | 
						// following an HTTP redirect. The sole argument is the number of
 | 
				
			||||||
 | 
						// requests that have already been made. If CheckRedirect returns
 | 
				
			||||||
 | 
						// an error, Client.Do will not make any further requests and return
 | 
				
			||||||
 | 
						// the error back it to the caller.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If CheckRedirect is nil, the Client uses its default policy,
 | 
				
			||||||
 | 
						// which is to stop after 10 consecutive requests.
 | 
				
			||||||
 | 
						CheckRedirect CheckRedirectFunc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Username specifies the user credential to add as an authorization header
 | 
				
			||||||
 | 
						Username string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Password is the password for the specified user to add as an authorization header
 | 
				
			||||||
 | 
						// to the request.
 | 
				
			||||||
 | 
						Password string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// HeaderTimeoutPerRequest specifies the time limit to wait for response
 | 
				
			||||||
 | 
						// header in a single request made by the Client. The timeout includes
 | 
				
			||||||
 | 
						// connection time, any redirects, and header wait time.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// For non-watch GET request, server returns the response body immediately.
 | 
				
			||||||
 | 
						// For PUT/POST/DELETE request, server will attempt to commit request
 | 
				
			||||||
 | 
						// before responding, which is expected to take `100ms + 2 * RTT`.
 | 
				
			||||||
 | 
						// For watch request, server returns the header immediately to notify Client
 | 
				
			||||||
 | 
						// watch start. But if server is behind some kind of proxy, the response
 | 
				
			||||||
 | 
						// header may be cached at proxy, and Client cannot rely on this behavior.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Especially, wait request will ignore this timeout.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// One API call may send multiple requests to different etcd servers until it
 | 
				
			||||||
 | 
						// succeeds. Use context of the API to specify the overall timeout.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// A HeaderTimeoutPerRequest of zero means no timeout.
 | 
				
			||||||
 | 
						HeaderTimeoutPerRequest time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// SelectionMode is an EndpointSelectionMode enum that specifies the
 | 
				
			||||||
 | 
						// policy for choosing the etcd cluster node to which requests are sent.
 | 
				
			||||||
 | 
						SelectionMode EndpointSelectionMode
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cfg *Config) transport() CancelableTransport {
 | 
				
			||||||
 | 
						if cfg.Transport == nil {
 | 
				
			||||||
 | 
							return DefaultTransport
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cfg.Transport
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cfg *Config) checkRedirect() CheckRedirectFunc {
 | 
				
			||||||
 | 
						if cfg.CheckRedirect == nil {
 | 
				
			||||||
 | 
							return DefaultCheckRedirect
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cfg.CheckRedirect
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CancelableTransport mimics net/http.Transport, but requires that
 | 
				
			||||||
 | 
					// the object also support request cancellation.
 | 
				
			||||||
 | 
					type CancelableTransport interface {
 | 
				
			||||||
 | 
						http.RoundTripper
 | 
				
			||||||
 | 
						CancelRequest(req *http.Request)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CheckRedirectFunc func(via int) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DefaultCheckRedirect follows up to 10 redirects, but no more.
 | 
				
			||||||
 | 
					var DefaultCheckRedirect CheckRedirectFunc = func(via int) error {
 | 
				
			||||||
 | 
						if via > 10 {
 | 
				
			||||||
 | 
							return ErrTooManyRedirects
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Client interface {
 | 
				
			||||||
 | 
						// Sync updates the internal cache of the etcd cluster's membership.
 | 
				
			||||||
 | 
						Sync(context.Context) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// AutoSync periodically calls Sync() every given interval.
 | 
				
			||||||
 | 
						// The recommended sync interval is 10 seconds to 1 minute, which does
 | 
				
			||||||
 | 
						// not bring too much overhead to server and makes client catch up the
 | 
				
			||||||
 | 
						// cluster change in time.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// The example to use it:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//  for {
 | 
				
			||||||
 | 
						//      err := client.AutoSync(ctx, 10*time.Second)
 | 
				
			||||||
 | 
						//      if err == context.DeadlineExceeded || err == context.Canceled {
 | 
				
			||||||
 | 
						//          break
 | 
				
			||||||
 | 
						//      }
 | 
				
			||||||
 | 
						//      log.Print(err)
 | 
				
			||||||
 | 
						//  }
 | 
				
			||||||
 | 
						AutoSync(context.Context, time.Duration) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Endpoints returns a copy of the current set of API endpoints used
 | 
				
			||||||
 | 
						// by Client to resolve HTTP requests. If Sync has ever been called,
 | 
				
			||||||
 | 
						// this may differ from the initial Endpoints provided in the Config.
 | 
				
			||||||
 | 
						Endpoints() []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// SetEndpoints sets the set of API endpoints used by Client to resolve
 | 
				
			||||||
 | 
						// HTTP requests. If the given endpoints are not valid, an error will be
 | 
				
			||||||
 | 
						// returned
 | 
				
			||||||
 | 
						SetEndpoints(eps []string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetVersion retrieves the current etcd server and cluster version
 | 
				
			||||||
 | 
						GetVersion(ctx context.Context) (*version.Versions, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						httpClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(cfg Config) (Client, error) {
 | 
				
			||||||
 | 
						c := &httpClusterClient{
 | 
				
			||||||
 | 
							clientFactory: newHTTPClientFactory(cfg.transport(), cfg.checkRedirect(), cfg.HeaderTimeoutPerRequest),
 | 
				
			||||||
 | 
							rand:          rand.New(rand.NewSource(int64(time.Now().Nanosecond()))),
 | 
				
			||||||
 | 
							selectionMode: cfg.SelectionMode,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if cfg.Username != "" {
 | 
				
			||||||
 | 
							c.credentials = &credentials{
 | 
				
			||||||
 | 
								username: cfg.Username,
 | 
				
			||||||
 | 
								password: cfg.Password,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := c.SetEndpoints(cfg.Endpoints); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return c, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpClient interface {
 | 
				
			||||||
 | 
						Do(context.Context, httpAction) (*http.Response, []byte, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newHTTPClientFactory(tr CancelableTransport, cr CheckRedirectFunc, headerTimeout time.Duration) httpClientFactory {
 | 
				
			||||||
 | 
						return func(ep url.URL) httpClient {
 | 
				
			||||||
 | 
							return &redirectFollowingHTTPClient{
 | 
				
			||||||
 | 
								checkRedirect: cr,
 | 
				
			||||||
 | 
								client: &simpleHTTPClient{
 | 
				
			||||||
 | 
									transport:     tr,
 | 
				
			||||||
 | 
									endpoint:      ep,
 | 
				
			||||||
 | 
									headerTimeout: headerTimeout,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type credentials struct {
 | 
				
			||||||
 | 
						username string
 | 
				
			||||||
 | 
						password string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpClientFactory func(url.URL) httpClient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpAction interface {
 | 
				
			||||||
 | 
						HTTPRequest(url.URL) *http.Request
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpClusterClient struct {
 | 
				
			||||||
 | 
						clientFactory httpClientFactory
 | 
				
			||||||
 | 
						endpoints     []url.URL
 | 
				
			||||||
 | 
						pinned        int
 | 
				
			||||||
 | 
						credentials   *credentials
 | 
				
			||||||
 | 
						sync.RWMutex
 | 
				
			||||||
 | 
						rand          *rand.Rand
 | 
				
			||||||
 | 
						selectionMode EndpointSelectionMode
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) getLeaderEndpoint(ctx context.Context, eps []url.URL) (string, error) {
 | 
				
			||||||
 | 
						ceps := make([]url.URL, len(eps))
 | 
				
			||||||
 | 
						copy(ceps, eps)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// To perform a lookup on the new endpoint list without using the current
 | 
				
			||||||
 | 
						// client, we'll copy it
 | 
				
			||||||
 | 
						clientCopy := &httpClusterClient{
 | 
				
			||||||
 | 
							clientFactory: c.clientFactory,
 | 
				
			||||||
 | 
							credentials:   c.credentials,
 | 
				
			||||||
 | 
							rand:          c.rand,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pinned:    0,
 | 
				
			||||||
 | 
							endpoints: ceps,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mAPI := NewMembersAPI(clientCopy)
 | 
				
			||||||
 | 
						leader, err := mAPI.Leader(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(leader.ClientURLs) == 0 {
 | 
				
			||||||
 | 
							return "", ErrNoLeaderEndpoint
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return leader.ClientURLs[0], nil // TODO: how to handle multiple client URLs?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) parseEndpoints(eps []string) ([]url.URL, error) {
 | 
				
			||||||
 | 
						if len(eps) == 0 {
 | 
				
			||||||
 | 
							return []url.URL{}, ErrNoEndpoints
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						neps := make([]url.URL, len(eps))
 | 
				
			||||||
 | 
						for i, ep := range eps {
 | 
				
			||||||
 | 
							u, err := url.Parse(ep)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return []url.URL{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							neps[i] = *u
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return neps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) SetEndpoints(eps []string) error {
 | 
				
			||||||
 | 
						neps, err := c.parseEndpoints(eps)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Lock()
 | 
				
			||||||
 | 
						defer c.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.endpoints = shuffleEndpoints(c.rand, neps)
 | 
				
			||||||
 | 
						// We're not doing anything for PrioritizeLeader here. This is
 | 
				
			||||||
 | 
						// due to not having a context meaning we can't call getLeaderEndpoint
 | 
				
			||||||
 | 
						// However, if you're using PrioritizeLeader, you've already been told
 | 
				
			||||||
 | 
						// to regularly call sync, where we do have a ctx, and can figure the
 | 
				
			||||||
 | 
						// leader. PrioritizeLeader is also quite a loose guarantee, so deal
 | 
				
			||||||
 | 
						// with it
 | 
				
			||||||
 | 
						c.pinned = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
 | 
				
			||||||
 | 
						action := act
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						leps := len(c.endpoints)
 | 
				
			||||||
 | 
						eps := make([]url.URL, leps)
 | 
				
			||||||
 | 
						n := copy(eps, c.endpoints)
 | 
				
			||||||
 | 
						pinned := c.pinned
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c.credentials != nil {
 | 
				
			||||||
 | 
							action = &authedAction{
 | 
				
			||||||
 | 
								act:         act,
 | 
				
			||||||
 | 
								credentials: *c.credentials,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						c.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if leps == 0 {
 | 
				
			||||||
 | 
							return nil, nil, ErrNoEndpoints
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if leps != n {
 | 
				
			||||||
 | 
							return nil, nil, errors.New("unable to pick endpoint: copy failed")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp *http.Response
 | 
				
			||||||
 | 
						var body []byte
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						cerr := &ClusterError{}
 | 
				
			||||||
 | 
						isOneShot := ctx.Value(&oneShotCtxValue) != nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := pinned; i < leps+pinned; i++ {
 | 
				
			||||||
 | 
							k := i % leps
 | 
				
			||||||
 | 
							hc := c.clientFactory(eps[k])
 | 
				
			||||||
 | 
							resp, body, err = hc.Do(ctx, action)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								cerr.Errors = append(cerr.Errors, err)
 | 
				
			||||||
 | 
								if err == ctx.Err() {
 | 
				
			||||||
 | 
									return nil, nil, ctx.Err()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err == context.Canceled || err == context.DeadlineExceeded {
 | 
				
			||||||
 | 
									return nil, nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if resp.StatusCode/100 == 5 {
 | 
				
			||||||
 | 
								switch resp.StatusCode {
 | 
				
			||||||
 | 
								case http.StatusInternalServerError, http.StatusServiceUnavailable:
 | 
				
			||||||
 | 
									// TODO: make sure this is a no leader response
 | 
				
			||||||
 | 
									cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s has no leader", eps[k].String()))
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s returns server error [%s]", eps[k].String(), http.StatusText(resp.StatusCode)))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								err = cerr.Errors[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if !isOneShot {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								c.Lock()
 | 
				
			||||||
 | 
								c.pinned = (k + 1) % leps
 | 
				
			||||||
 | 
								c.Unlock()
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if k != pinned {
 | 
				
			||||||
 | 
								c.Lock()
 | 
				
			||||||
 | 
								c.pinned = k
 | 
				
			||||||
 | 
								c.Unlock()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return resp, body, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil, nil, cerr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) Endpoints() []string {
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eps := make([]string, len(c.endpoints))
 | 
				
			||||||
 | 
						for i, ep := range c.endpoints {
 | 
				
			||||||
 | 
							eps[i] = ep.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return eps
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) Sync(ctx context.Context) error {
 | 
				
			||||||
 | 
						mAPI := NewMembersAPI(c)
 | 
				
			||||||
 | 
						ms, err := mAPI.List(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var eps []string
 | 
				
			||||||
 | 
						for _, m := range ms {
 | 
				
			||||||
 | 
							eps = append(eps, m.ClientURLs...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						neps, err := c.parseEndpoints(eps)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						npin := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch c.selectionMode {
 | 
				
			||||||
 | 
						case EndpointSelectionRandom:
 | 
				
			||||||
 | 
							c.RLock()
 | 
				
			||||||
 | 
							eq := endpointsEqual(c.endpoints, neps)
 | 
				
			||||||
 | 
							c.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if eq {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// When items in the endpoint list changes, we choose a new pin
 | 
				
			||||||
 | 
							neps = shuffleEndpoints(c.rand, neps)
 | 
				
			||||||
 | 
						case EndpointSelectionPrioritizeLeader:
 | 
				
			||||||
 | 
							nle, err := c.getLeaderEndpoint(ctx, neps)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return ErrNoLeaderEndpoint
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i, n := range neps {
 | 
				
			||||||
 | 
								if n.String() == nle {
 | 
				
			||||||
 | 
									npin = i
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid endpoint selection mode: %d", c.selectionMode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Lock()
 | 
				
			||||||
 | 
						defer c.Unlock()
 | 
				
			||||||
 | 
						c.endpoints = neps
 | 
				
			||||||
 | 
						c.pinned = npin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) AutoSync(ctx context.Context, interval time.Duration) error {
 | 
				
			||||||
 | 
						ticker := time.NewTicker(interval)
 | 
				
			||||||
 | 
						defer ticker.Stop()
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							err := c.Sync(ctx)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-ctx.Done():
 | 
				
			||||||
 | 
								return ctx.Err()
 | 
				
			||||||
 | 
							case <-ticker.C:
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *httpClusterClient) GetVersion(ctx context.Context) (*version.Versions, error) {
 | 
				
			||||||
 | 
						act := &getAction{Prefix: "/version"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, body, err := c.Do(ctx, act)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch resp.StatusCode {
 | 
				
			||||||
 | 
						case http.StatusOK:
 | 
				
			||||||
 | 
							if len(body) == 0 {
 | 
				
			||||||
 | 
								return nil, ErrEmptyBody
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							var vresp version.Versions
 | 
				
			||||||
 | 
							if err := json.Unmarshal(body, &vresp); err != nil {
 | 
				
			||||||
 | 
								return nil, ErrInvalidJSON
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return &vresp, nil
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							var etcdErr Error
 | 
				
			||||||
 | 
							if err := json.Unmarshal(body, &etcdErr); err != nil {
 | 
				
			||||||
 | 
								return nil, ErrInvalidJSON
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, etcdErr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type roundTripResponse struct {
 | 
				
			||||||
 | 
						resp *http.Response
 | 
				
			||||||
 | 
						err  error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type simpleHTTPClient struct {
 | 
				
			||||||
 | 
						transport     CancelableTransport
 | 
				
			||||||
 | 
						endpoint      url.URL
 | 
				
			||||||
 | 
						headerTimeout time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *simpleHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
 | 
				
			||||||
 | 
						req := act.HTTPRequest(c.endpoint)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := printcURL(req); err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isWait := false
 | 
				
			||||||
 | 
						if req != nil && req.URL != nil {
 | 
				
			||||||
 | 
							ws := req.URL.Query().Get("wait")
 | 
				
			||||||
 | 
							if len(ws) != 0 {
 | 
				
			||||||
 | 
								var err error
 | 
				
			||||||
 | 
								isWait, err = strconv.ParseBool(ws)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, nil, fmt.Errorf("wrong wait value %s (%v for %+v)", ws, err, req)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var hctx context.Context
 | 
				
			||||||
 | 
						var hcancel context.CancelFunc
 | 
				
			||||||
 | 
						if !isWait && c.headerTimeout > 0 {
 | 
				
			||||||
 | 
							hctx, hcancel = context.WithTimeout(ctx, c.headerTimeout)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							hctx, hcancel = context.WithCancel(ctx)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer hcancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reqcancel := requestCanceler(c.transport, req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rtchan := make(chan roundTripResponse, 1)
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							resp, err := c.transport.RoundTrip(req)
 | 
				
			||||||
 | 
							rtchan <- roundTripResponse{resp: resp, err: err}
 | 
				
			||||||
 | 
							close(rtchan)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp *http.Response
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case rtresp := <-rtchan:
 | 
				
			||||||
 | 
							resp, err = rtresp.resp, rtresp.err
 | 
				
			||||||
 | 
						case <-hctx.Done():
 | 
				
			||||||
 | 
							// cancel and wait for request to actually exit before continuing
 | 
				
			||||||
 | 
							reqcancel()
 | 
				
			||||||
 | 
							rtresp := <-rtchan
 | 
				
			||||||
 | 
							resp = rtresp.resp
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case ctx.Err() != nil:
 | 
				
			||||||
 | 
								err = ctx.Err()
 | 
				
			||||||
 | 
							case hctx.Err() != nil:
 | 
				
			||||||
 | 
								err = fmt.Errorf("client: endpoint %s exceeded header timeout", c.endpoint.String())
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								panic("failed to get error from context")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// always check for resp nil-ness to deal with possible
 | 
				
			||||||
 | 
						// race conditions between channels above
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if resp != nil {
 | 
				
			||||||
 | 
								resp.Body.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var body []byte
 | 
				
			||||||
 | 
						done := make(chan struct{})
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							body, err = ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
							done <- struct{}{}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case <-ctx.Done():
 | 
				
			||||||
 | 
							resp.Body.Close()
 | 
				
			||||||
 | 
							<-done
 | 
				
			||||||
 | 
							return nil, nil, ctx.Err()
 | 
				
			||||||
 | 
						case <-done:
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp, body, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type authedAction struct {
 | 
				
			||||||
 | 
						act         httpAction
 | 
				
			||||||
 | 
						credentials credentials
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *authedAction) HTTPRequest(url url.URL) *http.Request {
 | 
				
			||||||
 | 
						r := a.act.HTTPRequest(url)
 | 
				
			||||||
 | 
						r.SetBasicAuth(a.credentials.username, a.credentials.password)
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type redirectFollowingHTTPClient struct {
 | 
				
			||||||
 | 
						client        httpClient
 | 
				
			||||||
 | 
						checkRedirect CheckRedirectFunc
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *redirectFollowingHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
 | 
				
			||||||
 | 
						next := act
 | 
				
			||||||
 | 
						for i := 0; i < 100; i++ {
 | 
				
			||||||
 | 
							if i > 0 {
 | 
				
			||||||
 | 
								if err := r.checkRedirect(i); err != nil {
 | 
				
			||||||
 | 
									return nil, nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							resp, body, err := r.client.Do(ctx, next)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if resp.StatusCode/100 == 3 {
 | 
				
			||||||
 | 
								hdr := resp.Header.Get("Location")
 | 
				
			||||||
 | 
								if hdr == "" {
 | 
				
			||||||
 | 
									return nil, nil, fmt.Errorf("Location header not set")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								loc, err := url.Parse(hdr)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, nil, fmt.Errorf("Location header not valid URL: %s", hdr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								next = &redirectedHTTPAction{
 | 
				
			||||||
 | 
									action:   act,
 | 
				
			||||||
 | 
									location: *loc,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return resp, body, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil, nil, errTooManyRedirectChecks
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type redirectedHTTPAction struct {
 | 
				
			||||||
 | 
						action   httpAction
 | 
				
			||||||
 | 
						location url.URL
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *redirectedHTTPAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						orig := r.action.HTTPRequest(ep)
 | 
				
			||||||
 | 
						orig.URL = &r.location
 | 
				
			||||||
 | 
						return orig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func shuffleEndpoints(r *rand.Rand, eps []url.URL) []url.URL {
 | 
				
			||||||
 | 
						// copied from Go 1.9<= rand.Rand.Perm
 | 
				
			||||||
 | 
						n := len(eps)
 | 
				
			||||||
 | 
						p := make([]int, n)
 | 
				
			||||||
 | 
						for i := 0; i < n; i++ {
 | 
				
			||||||
 | 
							j := r.Intn(i + 1)
 | 
				
			||||||
 | 
							p[i] = p[j]
 | 
				
			||||||
 | 
							p[j] = i
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						neps := make([]url.URL, n)
 | 
				
			||||||
 | 
						for i, k := range p {
 | 
				
			||||||
 | 
							neps[i] = eps[k]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return neps
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func endpointsEqual(left, right []url.URL) bool {
 | 
				
			||||||
 | 
						if len(left) != len(right) {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sLeft := make([]string, len(left))
 | 
				
			||||||
 | 
						sRight := make([]string, len(right))
 | 
				
			||||||
 | 
						for i, l := range left {
 | 
				
			||||||
 | 
							sLeft[i] = l.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, r := range right {
 | 
				
			||||||
 | 
							sRight[i] = r.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sort.Strings(sLeft)
 | 
				
			||||||
 | 
						sort.Strings(sRight)
 | 
				
			||||||
 | 
						for i := range sLeft {
 | 
				
			||||||
 | 
							if sLeft[i] != sRight[i] {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								vendor/github.com/coreos/etcd/client/cluster_error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/coreos/etcd/client/cluster_error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ClusterError struct {
 | 
				
			||||||
 | 
						Errors []error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ce *ClusterError) Error() string {
 | 
				
			||||||
 | 
						s := ErrClusterUnavailable.Error()
 | 
				
			||||||
 | 
						for i, e := range ce.Errors {
 | 
				
			||||||
 | 
							s += fmt.Sprintf("; error #%d: %s\n", i, e)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ce *ClusterError) Detail() string {
 | 
				
			||||||
 | 
						s := ""
 | 
				
			||||||
 | 
						for i, e := range ce.Errors {
 | 
				
			||||||
 | 
							s += fmt.Sprintf("error #%d: %s\n", i, e)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								vendor/github.com/coreos/etcd/client/curl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vendor/github.com/coreos/etcd/client/curl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						cURLDebug = false
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func EnablecURLDebug() {
 | 
				
			||||||
 | 
						cURLDebug = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DisablecURLDebug() {
 | 
				
			||||||
 | 
						cURLDebug = false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// printcURL prints the cURL equivalent request to stderr.
 | 
				
			||||||
 | 
					// It returns an error if the body of the request cannot
 | 
				
			||||||
 | 
					// be read.
 | 
				
			||||||
 | 
					// The caller MUST cancel the request if there is an error.
 | 
				
			||||||
 | 
					func printcURL(req *http.Request) error {
 | 
				
			||||||
 | 
						if !cURLDebug {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							command string
 | 
				
			||||||
 | 
							b       []byte
 | 
				
			||||||
 | 
							err     error
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if req.URL != nil {
 | 
				
			||||||
 | 
							command = fmt.Sprintf("curl -X %s %s", req.Method, req.URL.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if req.Body != nil {
 | 
				
			||||||
 | 
							b, err = ioutil.ReadAll(req.Body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							command += fmt.Sprintf(" -d %q", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Fprintf(os.Stderr, "cURL Command: %s\n", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// reset body
 | 
				
			||||||
 | 
						body := bytes.NewBuffer(b)
 | 
				
			||||||
 | 
						req.Body = ioutil.NopCloser(body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								vendor/github.com/coreos/etcd/client/discover.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/coreos/etcd/client/discover.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/coreos/etcd/pkg/srv"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Discoverer is an interface that wraps the Discover method.
 | 
				
			||||||
 | 
					type Discoverer interface {
 | 
				
			||||||
 | 
						// Discover looks up the etcd servers for the domain.
 | 
				
			||||||
 | 
						Discover(domain string) ([]string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type srvDiscover struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewSRVDiscover constructs a new Discoverer that uses the stdlib to lookup SRV records.
 | 
				
			||||||
 | 
					func NewSRVDiscover() Discoverer {
 | 
				
			||||||
 | 
						return &srvDiscover{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *srvDiscover) Discover(domain string) ([]string, error) {
 | 
				
			||||||
 | 
						srvs, err := srv.GetClient("etcd-client", domain)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return srvs.Endpoints, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								vendor/github.com/coreos/etcd/client/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/github.com/coreos/etcd/client/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Package client provides bindings for the etcd APIs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Create a Config and exchange it for a Client:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						import (
 | 
				
			||||||
 | 
							"net/http"
 | 
				
			||||||
 | 
							"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							"github.com/coreos/etcd/client"
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg := client.Config{
 | 
				
			||||||
 | 
							Endpoints: []string{"http://127.0.0.1:2379"},
 | 
				
			||||||
 | 
							Transport: DefaultTransport,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c, err := client.New(cfg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// handle error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Clients are safe for concurrent use by multiple goroutines.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Create a KeysAPI using the Client, then use it to interact with etcd:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kAPI := client.NewKeysAPI(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// create a new key /foo with the value "bar"
 | 
				
			||||||
 | 
						_, err = kAPI.Create(context.Background(), "/foo", "bar")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// handle error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// delete the newly created key only if the value is still "bar"
 | 
				
			||||||
 | 
						_, err = kAPI.Delete(context.Background(), "/foo", &DeleteOptions{PrevValue: "bar"})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// handle error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use a custom context to set timeouts on your operations:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						import "time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 | 
				
			||||||
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set a new key, ignoring its previous state
 | 
				
			||||||
 | 
						_, err := kAPI.Set(ctx, "/ping", "pong", nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if err == context.DeadlineExceeded {
 | 
				
			||||||
 | 
								// request took longer than 5s
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// handle error
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
							
								
								
									
										17
									
								
								vendor/github.com/coreos/etcd/client/integration/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/coreos/etcd/client/integration/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					// Copyright 2016 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package integration implements tests built upon embedded etcd, focusing on
 | 
				
			||||||
 | 
					// the correctness of the etcd v2 client.
 | 
				
			||||||
 | 
					package integration
 | 
				
			||||||
							
								
								
									
										5218
									
								
								vendor/github.com/coreos/etcd/client/keys.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5218
									
								
								vendor/github.com/coreos/etcd/client/keys.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										681
									
								
								vendor/github.com/coreos/etcd/client/keys.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										681
									
								
								vendor/github.com/coreos/etcd/client/keys.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,681 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:generate codecgen -d 1819 -r "Node|Response|Nodes" -o keys.generated.go keys.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/coreos/etcd/pkg/pathutil"
 | 
				
			||||||
 | 
						"github.com/ugorji/go/codec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ErrorCodeKeyNotFound  = 100
 | 
				
			||||||
 | 
						ErrorCodeTestFailed   = 101
 | 
				
			||||||
 | 
						ErrorCodeNotFile      = 102
 | 
				
			||||||
 | 
						ErrorCodeNotDir       = 104
 | 
				
			||||||
 | 
						ErrorCodeNodeExist    = 105
 | 
				
			||||||
 | 
						ErrorCodeRootROnly    = 107
 | 
				
			||||||
 | 
						ErrorCodeDirNotEmpty  = 108
 | 
				
			||||||
 | 
						ErrorCodeUnauthorized = 110
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ErrorCodePrevValueRequired = 201
 | 
				
			||||||
 | 
						ErrorCodeTTLNaN            = 202
 | 
				
			||||||
 | 
						ErrorCodeIndexNaN          = 203
 | 
				
			||||||
 | 
						ErrorCodeInvalidField      = 209
 | 
				
			||||||
 | 
						ErrorCodeInvalidForm       = 210
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ErrorCodeRaftInternal = 300
 | 
				
			||||||
 | 
						ErrorCodeLeaderElect  = 301
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ErrorCodeWatcherCleared    = 400
 | 
				
			||||||
 | 
						ErrorCodeEventIndexCleared = 401
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Error struct {
 | 
				
			||||||
 | 
						Code    int    `json:"errorCode"`
 | 
				
			||||||
 | 
						Message string `json:"message"`
 | 
				
			||||||
 | 
						Cause   string `json:"cause"`
 | 
				
			||||||
 | 
						Index   uint64 `json:"index"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e Error) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%v: %v (%v) [%v]", e.Code, e.Message, e.Cause, e.Index)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						ErrInvalidJSON = errors.New("client: response is invalid json. The endpoint is probably not valid etcd cluster endpoint.")
 | 
				
			||||||
 | 
						ErrEmptyBody   = errors.New("client: response body is empty")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PrevExistType is used to define an existence condition when setting
 | 
				
			||||||
 | 
					// or deleting Nodes.
 | 
				
			||||||
 | 
					type PrevExistType string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						PrevIgnore  = PrevExistType("")
 | 
				
			||||||
 | 
						PrevExist   = PrevExistType("true")
 | 
				
			||||||
 | 
						PrevNoExist = PrevExistType("false")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						defaultV2KeysPrefix = "/v2/keys"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewKeysAPI builds a KeysAPI that interacts with etcd's key-value
 | 
				
			||||||
 | 
					// API over HTTP.
 | 
				
			||||||
 | 
					func NewKeysAPI(c Client) KeysAPI {
 | 
				
			||||||
 | 
						return NewKeysAPIWithPrefix(c, defaultV2KeysPrefix)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewKeysAPIWithPrefix acts like NewKeysAPI, but allows the caller
 | 
				
			||||||
 | 
					// to provide a custom base URL path. This should only be used in
 | 
				
			||||||
 | 
					// very rare cases.
 | 
				
			||||||
 | 
					func NewKeysAPIWithPrefix(c Client, p string) KeysAPI {
 | 
				
			||||||
 | 
						return &httpKeysAPI{
 | 
				
			||||||
 | 
							client: c,
 | 
				
			||||||
 | 
							prefix: p,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type KeysAPI interface {
 | 
				
			||||||
 | 
						// Get retrieves a set of Nodes from etcd
 | 
				
			||||||
 | 
						Get(ctx context.Context, key string, opts *GetOptions) (*Response, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set assigns a new value to a Node identified by a given key. The caller
 | 
				
			||||||
 | 
						// may define a set of conditions in the SetOptions. If SetOptions.Dir=true
 | 
				
			||||||
 | 
						// then value is ignored.
 | 
				
			||||||
 | 
						Set(ctx context.Context, key, value string, opts *SetOptions) (*Response, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete removes a Node identified by the given key, optionally destroying
 | 
				
			||||||
 | 
						// all of its children as well. The caller may define a set of required
 | 
				
			||||||
 | 
						// conditions in an DeleteOptions object.
 | 
				
			||||||
 | 
						Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create is an alias for Set w/ PrevExist=false
 | 
				
			||||||
 | 
						Create(ctx context.Context, key, value string) (*Response, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// CreateInOrder is used to atomically create in-order keys within the given directory.
 | 
				
			||||||
 | 
						CreateInOrder(ctx context.Context, dir, value string, opts *CreateInOrderOptions) (*Response, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update is an alias for Set w/ PrevExist=true
 | 
				
			||||||
 | 
						Update(ctx context.Context, key, value string) (*Response, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Watcher builds a new Watcher targeted at a specific Node identified
 | 
				
			||||||
 | 
						// by the given key. The Watcher may be configured at creation time
 | 
				
			||||||
 | 
						// through a WatcherOptions object. The returned Watcher is designed
 | 
				
			||||||
 | 
						// to emit events that happen to a Node, and optionally to its children.
 | 
				
			||||||
 | 
						Watcher(key string, opts *WatcherOptions) Watcher
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WatcherOptions struct {
 | 
				
			||||||
 | 
						// AfterIndex defines the index after-which the Watcher should
 | 
				
			||||||
 | 
						// start emitting events. For example, if a value of 5 is
 | 
				
			||||||
 | 
						// provided, the first event will have an index >= 6.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Setting AfterIndex to 0 (default) means that the Watcher
 | 
				
			||||||
 | 
						// should start watching for events starting at the current
 | 
				
			||||||
 | 
						// index, whatever that may be.
 | 
				
			||||||
 | 
						AfterIndex uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Recursive specifies whether or not the Watcher should emit
 | 
				
			||||||
 | 
						// events that occur in children of the given keyspace. If set
 | 
				
			||||||
 | 
						// to false (default), events will be limited to those that
 | 
				
			||||||
 | 
						// occur for the exact key.
 | 
				
			||||||
 | 
						Recursive bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CreateInOrderOptions struct {
 | 
				
			||||||
 | 
						// TTL defines a period of time after-which the Node should
 | 
				
			||||||
 | 
						// expire and no longer exist. Values <= 0 are ignored. Given
 | 
				
			||||||
 | 
						// that the zero-value is ignored, TTL cannot be used to set
 | 
				
			||||||
 | 
						// a TTL of 0.
 | 
				
			||||||
 | 
						TTL time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SetOptions struct {
 | 
				
			||||||
 | 
						// PrevValue specifies what the current value of the Node must
 | 
				
			||||||
 | 
						// be in order for the Set operation to succeed.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Leaving this field empty means that the caller wishes to
 | 
				
			||||||
 | 
						// ignore the current value of the Node. This cannot be used
 | 
				
			||||||
 | 
						// to compare the Node's current value to an empty string.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// PrevValue is ignored if Dir=true
 | 
				
			||||||
 | 
						PrevValue string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PrevIndex indicates what the current ModifiedIndex of the
 | 
				
			||||||
 | 
						// Node must be in order for the Set operation to succeed.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If PrevIndex is set to 0 (default), no comparison is made.
 | 
				
			||||||
 | 
						PrevIndex uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PrevExist specifies whether the Node must currently exist
 | 
				
			||||||
 | 
						// (PrevExist) or not (PrevNoExist). If the caller does not
 | 
				
			||||||
 | 
						// care about existence, set PrevExist to PrevIgnore, or simply
 | 
				
			||||||
 | 
						// leave it unset.
 | 
				
			||||||
 | 
						PrevExist PrevExistType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TTL defines a period of time after-which the Node should
 | 
				
			||||||
 | 
						// expire and no longer exist. Values <= 0 are ignored. Given
 | 
				
			||||||
 | 
						// that the zero-value is ignored, TTL cannot be used to set
 | 
				
			||||||
 | 
						// a TTL of 0.
 | 
				
			||||||
 | 
						TTL time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Refresh set to true means a TTL value can be updated
 | 
				
			||||||
 | 
						// without firing a watch or changing the node value. A
 | 
				
			||||||
 | 
						// value must not be provided when refreshing a key.
 | 
				
			||||||
 | 
						Refresh bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Dir specifies whether or not this Node should be created as a directory.
 | 
				
			||||||
 | 
						Dir bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// NoValueOnSuccess specifies whether the response contains the current value of the Node.
 | 
				
			||||||
 | 
						// If set, the response will only contain the current value when the request fails.
 | 
				
			||||||
 | 
						NoValueOnSuccess bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetOptions struct {
 | 
				
			||||||
 | 
						// Recursive defines whether or not all children of the Node
 | 
				
			||||||
 | 
						// should be returned.
 | 
				
			||||||
 | 
						Recursive bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sort instructs the server whether or not to sort the Nodes.
 | 
				
			||||||
 | 
						// If true, the Nodes are sorted alphabetically by key in
 | 
				
			||||||
 | 
						// ascending order (A to z). If false (default), the Nodes will
 | 
				
			||||||
 | 
						// not be sorted and the ordering used should not be considered
 | 
				
			||||||
 | 
						// predictable.
 | 
				
			||||||
 | 
						Sort bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Quorum specifies whether it gets the latest committed value that
 | 
				
			||||||
 | 
						// has been applied in quorum of members, which ensures external
 | 
				
			||||||
 | 
						// consistency (or linearizability).
 | 
				
			||||||
 | 
						Quorum bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DeleteOptions struct {
 | 
				
			||||||
 | 
						// PrevValue specifies what the current value of the Node must
 | 
				
			||||||
 | 
						// be in order for the Delete operation to succeed.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Leaving this field empty means that the caller wishes to
 | 
				
			||||||
 | 
						// ignore the current value of the Node. This cannot be used
 | 
				
			||||||
 | 
						// to compare the Node's current value to an empty string.
 | 
				
			||||||
 | 
						PrevValue string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PrevIndex indicates what the current ModifiedIndex of the
 | 
				
			||||||
 | 
						// Node must be in order for the Delete operation to succeed.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If PrevIndex is set to 0 (default), no comparison is made.
 | 
				
			||||||
 | 
						PrevIndex uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Recursive defines whether or not all children of the Node
 | 
				
			||||||
 | 
						// should be deleted. If set to true, all children of the Node
 | 
				
			||||||
 | 
						// identified by the given key will be deleted. If left unset
 | 
				
			||||||
 | 
						// or explicitly set to false, only a single Node will be
 | 
				
			||||||
 | 
						// deleted.
 | 
				
			||||||
 | 
						Recursive bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Dir specifies whether or not this Node should be removed as a directory.
 | 
				
			||||||
 | 
						Dir bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Watcher interface {
 | 
				
			||||||
 | 
						// Next blocks until an etcd event occurs, then returns a Response
 | 
				
			||||||
 | 
						// representing that event. The behavior of Next depends on the
 | 
				
			||||||
 | 
						// WatcherOptions used to construct the Watcher. Next is designed to
 | 
				
			||||||
 | 
						// be called repeatedly, each time blocking until a subsequent event
 | 
				
			||||||
 | 
						// is available.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If the provided context is cancelled, Next will return a non-nil
 | 
				
			||||||
 | 
						// error. Any other failures encountered while waiting for the next
 | 
				
			||||||
 | 
						// event (connection issues, deserialization failures, etc) will
 | 
				
			||||||
 | 
						// also result in a non-nil error.
 | 
				
			||||||
 | 
						Next(context.Context) (*Response, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Response struct {
 | 
				
			||||||
 | 
						// Action is the name of the operation that occurred. Possible values
 | 
				
			||||||
 | 
						// include get, set, delete, update, create, compareAndSwap,
 | 
				
			||||||
 | 
						// compareAndDelete and expire.
 | 
				
			||||||
 | 
						Action string `json:"action"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Node represents the state of the relevant etcd Node.
 | 
				
			||||||
 | 
						Node *Node `json:"node"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PrevNode represents the previous state of the Node. PrevNode is non-nil
 | 
				
			||||||
 | 
						// only if the Node existed before the action occurred and the action
 | 
				
			||||||
 | 
						// caused a change to the Node.
 | 
				
			||||||
 | 
						PrevNode *Node `json:"prevNode"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Index holds the cluster-level index at the time the Response was generated.
 | 
				
			||||||
 | 
						// This index is not tied to the Node(s) contained in this Response.
 | 
				
			||||||
 | 
						Index uint64 `json:"-"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ClusterID holds the cluster-level ID reported by the server.  This
 | 
				
			||||||
 | 
						// should be different for different etcd clusters.
 | 
				
			||||||
 | 
						ClusterID string `json:"-"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Node struct {
 | 
				
			||||||
 | 
						// Key represents the unique location of this Node (e.g. "/foo/bar").
 | 
				
			||||||
 | 
						Key string `json:"key"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Dir reports whether node describes a directory.
 | 
				
			||||||
 | 
						Dir bool `json:"dir,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Value is the current data stored on this Node. If this Node
 | 
				
			||||||
 | 
						// is a directory, Value will be empty.
 | 
				
			||||||
 | 
						Value string `json:"value"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Nodes holds the children of this Node, only if this Node is a directory.
 | 
				
			||||||
 | 
						// This slice of will be arbitrarily deep (children, grandchildren, great-
 | 
				
			||||||
 | 
						// grandchildren, etc.) if a recursive Get or Watch request were made.
 | 
				
			||||||
 | 
						Nodes Nodes `json:"nodes"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// CreatedIndex is the etcd index at-which this Node was created.
 | 
				
			||||||
 | 
						CreatedIndex uint64 `json:"createdIndex"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ModifiedIndex is the etcd index at-which this Node was last modified.
 | 
				
			||||||
 | 
						ModifiedIndex uint64 `json:"modifiedIndex"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Expiration is the server side expiration time of the key.
 | 
				
			||||||
 | 
						Expiration *time.Time `json:"expiration,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TTL is the time to live of the key in second.
 | 
				
			||||||
 | 
						TTL int64 `json:"ttl,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *Node) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("{Key: %s, CreatedIndex: %d, ModifiedIndex: %d, TTL: %d}", n.Key, n.CreatedIndex, n.ModifiedIndex, n.TTL)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TTLDuration returns the Node's TTL as a time.Duration object
 | 
				
			||||||
 | 
					func (n *Node) TTLDuration() time.Duration {
 | 
				
			||||||
 | 
						return time.Duration(n.TTL) * time.Second
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Nodes []*Node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// interfaces for sorting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns Nodes) Len() int           { return len(ns) }
 | 
				
			||||||
 | 
					func (ns Nodes) Less(i, j int) bool { return ns[i].Key < ns[j].Key }
 | 
				
			||||||
 | 
					func (ns Nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpKeysAPI struct {
 | 
				
			||||||
 | 
						client httpClient
 | 
				
			||||||
 | 
						prefix string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) Set(ctx context.Context, key, val string, opts *SetOptions) (*Response, error) {
 | 
				
			||||||
 | 
						act := &setAction{
 | 
				
			||||||
 | 
							Prefix: k.prefix,
 | 
				
			||||||
 | 
							Key:    key,
 | 
				
			||||||
 | 
							Value:  val,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts != nil {
 | 
				
			||||||
 | 
							act.PrevValue = opts.PrevValue
 | 
				
			||||||
 | 
							act.PrevIndex = opts.PrevIndex
 | 
				
			||||||
 | 
							act.PrevExist = opts.PrevExist
 | 
				
			||||||
 | 
							act.TTL = opts.TTL
 | 
				
			||||||
 | 
							act.Refresh = opts.Refresh
 | 
				
			||||||
 | 
							act.Dir = opts.Dir
 | 
				
			||||||
 | 
							act.NoValueOnSuccess = opts.NoValueOnSuccess
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						doCtx := ctx
 | 
				
			||||||
 | 
						if act.PrevExist == PrevNoExist {
 | 
				
			||||||
 | 
							doCtx = context.WithValue(doCtx, &oneShotCtxValue, &oneShotCtxValue)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp, body, err := k.client.Do(doCtx, act)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) Create(ctx context.Context, key, val string) (*Response, error) {
 | 
				
			||||||
 | 
						return k.Set(ctx, key, val, &SetOptions{PrevExist: PrevNoExist})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) CreateInOrder(ctx context.Context, dir, val string, opts *CreateInOrderOptions) (*Response, error) {
 | 
				
			||||||
 | 
						act := &createInOrderAction{
 | 
				
			||||||
 | 
							Prefix: k.prefix,
 | 
				
			||||||
 | 
							Dir:    dir,
 | 
				
			||||||
 | 
							Value:  val,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts != nil {
 | 
				
			||||||
 | 
							act.TTL = opts.TTL
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, body, err := k.client.Do(ctx, act)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) Update(ctx context.Context, key, val string) (*Response, error) {
 | 
				
			||||||
 | 
						return k.Set(ctx, key, val, &SetOptions{PrevExist: PrevExist})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error) {
 | 
				
			||||||
 | 
						act := &deleteAction{
 | 
				
			||||||
 | 
							Prefix: k.prefix,
 | 
				
			||||||
 | 
							Key:    key,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts != nil {
 | 
				
			||||||
 | 
							act.PrevValue = opts.PrevValue
 | 
				
			||||||
 | 
							act.PrevIndex = opts.PrevIndex
 | 
				
			||||||
 | 
							act.Dir = opts.Dir
 | 
				
			||||||
 | 
							act.Recursive = opts.Recursive
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						doCtx := context.WithValue(ctx, &oneShotCtxValue, &oneShotCtxValue)
 | 
				
			||||||
 | 
						resp, body, err := k.client.Do(doCtx, act)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) Get(ctx context.Context, key string, opts *GetOptions) (*Response, error) {
 | 
				
			||||||
 | 
						act := &getAction{
 | 
				
			||||||
 | 
							Prefix: k.prefix,
 | 
				
			||||||
 | 
							Key:    key,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts != nil {
 | 
				
			||||||
 | 
							act.Recursive = opts.Recursive
 | 
				
			||||||
 | 
							act.Sorted = opts.Sort
 | 
				
			||||||
 | 
							act.Quorum = opts.Quorum
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, body, err := k.client.Do(ctx, act)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *httpKeysAPI) Watcher(key string, opts *WatcherOptions) Watcher {
 | 
				
			||||||
 | 
						act := waitAction{
 | 
				
			||||||
 | 
							Prefix: k.prefix,
 | 
				
			||||||
 | 
							Key:    key,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts != nil {
 | 
				
			||||||
 | 
							act.Recursive = opts.Recursive
 | 
				
			||||||
 | 
							if opts.AfterIndex > 0 {
 | 
				
			||||||
 | 
								act.WaitIndex = opts.AfterIndex + 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &httpWatcher{
 | 
				
			||||||
 | 
							client:   k.client,
 | 
				
			||||||
 | 
							nextWait: act,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpWatcher struct {
 | 
				
			||||||
 | 
						client   httpClient
 | 
				
			||||||
 | 
						nextWait waitAction
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *httpWatcher) Next(ctx context.Context) (*Response, error) {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							httpresp, body, err := hw.client.Do(ctx, &hw.nextWait)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							resp, err := unmarshalHTTPResponse(httpresp.StatusCode, httpresp.Header, body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if err == ErrEmptyBody {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							hw.nextWait.WaitIndex = resp.Node.ModifiedIndex + 1
 | 
				
			||||||
 | 
							return resp, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// v2KeysURL forms a URL representing the location of a key.
 | 
				
			||||||
 | 
					// The endpoint argument represents the base URL of an etcd
 | 
				
			||||||
 | 
					// server. The prefix is the path needed to route from the
 | 
				
			||||||
 | 
					// provided endpoint's path to the root of the keys API
 | 
				
			||||||
 | 
					// (typically "/v2/keys").
 | 
				
			||||||
 | 
					func v2KeysURL(ep url.URL, prefix, key string) *url.URL {
 | 
				
			||||||
 | 
						// We concatenate all parts together manually. We cannot use
 | 
				
			||||||
 | 
						// path.Join because it does not reserve trailing slash.
 | 
				
			||||||
 | 
						// We call CanonicalURLPath to further cleanup the path.
 | 
				
			||||||
 | 
						if prefix != "" && prefix[0] != '/' {
 | 
				
			||||||
 | 
							prefix = "/" + prefix
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if key != "" && key[0] != '/' {
 | 
				
			||||||
 | 
							key = "/" + key
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ep.Path = pathutil.CanonicalURLPath(ep.Path + prefix + key)
 | 
				
			||||||
 | 
						return &ep
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type getAction struct {
 | 
				
			||||||
 | 
						Prefix    string
 | 
				
			||||||
 | 
						Key       string
 | 
				
			||||||
 | 
						Recursive bool
 | 
				
			||||||
 | 
						Sorted    bool
 | 
				
			||||||
 | 
						Quorum    bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *getAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2KeysURL(ep, g.Prefix, g.Key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := u.Query()
 | 
				
			||||||
 | 
						params.Set("recursive", strconv.FormatBool(g.Recursive))
 | 
				
			||||||
 | 
						params.Set("sorted", strconv.FormatBool(g.Sorted))
 | 
				
			||||||
 | 
						params.Set("quorum", strconv.FormatBool(g.Quorum))
 | 
				
			||||||
 | 
						u.RawQuery = params.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("GET", u.String(), nil)
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type waitAction struct {
 | 
				
			||||||
 | 
						Prefix    string
 | 
				
			||||||
 | 
						Key       string
 | 
				
			||||||
 | 
						WaitIndex uint64
 | 
				
			||||||
 | 
						Recursive bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w *waitAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2KeysURL(ep, w.Prefix, w.Key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := u.Query()
 | 
				
			||||||
 | 
						params.Set("wait", "true")
 | 
				
			||||||
 | 
						params.Set("waitIndex", strconv.FormatUint(w.WaitIndex, 10))
 | 
				
			||||||
 | 
						params.Set("recursive", strconv.FormatBool(w.Recursive))
 | 
				
			||||||
 | 
						u.RawQuery = params.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("GET", u.String(), nil)
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type setAction struct {
 | 
				
			||||||
 | 
						Prefix           string
 | 
				
			||||||
 | 
						Key              string
 | 
				
			||||||
 | 
						Value            string
 | 
				
			||||||
 | 
						PrevValue        string
 | 
				
			||||||
 | 
						PrevIndex        uint64
 | 
				
			||||||
 | 
						PrevExist        PrevExistType
 | 
				
			||||||
 | 
						TTL              time.Duration
 | 
				
			||||||
 | 
						Refresh          bool
 | 
				
			||||||
 | 
						Dir              bool
 | 
				
			||||||
 | 
						NoValueOnSuccess bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *setAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2KeysURL(ep, a.Prefix, a.Key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := u.Query()
 | 
				
			||||||
 | 
						form := url.Values{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// we're either creating a directory or setting a key
 | 
				
			||||||
 | 
						if a.Dir {
 | 
				
			||||||
 | 
							params.Set("dir", strconv.FormatBool(a.Dir))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// These options are only valid for setting a key
 | 
				
			||||||
 | 
							if a.PrevValue != "" {
 | 
				
			||||||
 | 
								params.Set("prevValue", a.PrevValue)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							form.Add("value", a.Value)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Options which apply to both setting a key and creating a dir
 | 
				
			||||||
 | 
						if a.PrevIndex != 0 {
 | 
				
			||||||
 | 
							params.Set("prevIndex", strconv.FormatUint(a.PrevIndex, 10))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.PrevExist != PrevIgnore {
 | 
				
			||||||
 | 
							params.Set("prevExist", string(a.PrevExist))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.TTL > 0 {
 | 
				
			||||||
 | 
							form.Add("ttl", strconv.FormatUint(uint64(a.TTL.Seconds()), 10))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if a.Refresh {
 | 
				
			||||||
 | 
							form.Add("refresh", "true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.NoValueOnSuccess {
 | 
				
			||||||
 | 
							params.Set("noValueOnSuccess", strconv.FormatBool(a.NoValueOnSuccess))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.RawQuery = params.Encode()
 | 
				
			||||||
 | 
						body := strings.NewReader(form.Encode())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("PUT", u.String(), body)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type deleteAction struct {
 | 
				
			||||||
 | 
						Prefix    string
 | 
				
			||||||
 | 
						Key       string
 | 
				
			||||||
 | 
						PrevValue string
 | 
				
			||||||
 | 
						PrevIndex uint64
 | 
				
			||||||
 | 
						Dir       bool
 | 
				
			||||||
 | 
						Recursive bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *deleteAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2KeysURL(ep, a.Prefix, a.Key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := u.Query()
 | 
				
			||||||
 | 
						if a.PrevValue != "" {
 | 
				
			||||||
 | 
							params.Set("prevValue", a.PrevValue)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.PrevIndex != 0 {
 | 
				
			||||||
 | 
							params.Set("prevIndex", strconv.FormatUint(a.PrevIndex, 10))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.Dir {
 | 
				
			||||||
 | 
							params.Set("dir", "true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.Recursive {
 | 
				
			||||||
 | 
							params.Set("recursive", "true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						u.RawQuery = params.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("DELETE", u.String(), nil)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type createInOrderAction struct {
 | 
				
			||||||
 | 
						Prefix string
 | 
				
			||||||
 | 
						Dir    string
 | 
				
			||||||
 | 
						Value  string
 | 
				
			||||||
 | 
						TTL    time.Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *createInOrderAction) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2KeysURL(ep, a.Prefix, a.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						form := url.Values{}
 | 
				
			||||||
 | 
						form.Add("value", a.Value)
 | 
				
			||||||
 | 
						if a.TTL > 0 {
 | 
				
			||||||
 | 
							form.Add("ttl", strconv.FormatUint(uint64(a.TTL.Seconds()), 10))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						body := strings.NewReader(form.Encode())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("POST", u.String(), body)
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Response, err error) {
 | 
				
			||||||
 | 
						switch code {
 | 
				
			||||||
 | 
						case http.StatusOK, http.StatusCreated:
 | 
				
			||||||
 | 
							if len(body) == 0 {
 | 
				
			||||||
 | 
								return nil, ErrEmptyBody
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							res, err = unmarshalSuccessfulKeysResponse(header, body)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							err = unmarshalFailedKeysResponse(body)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response, error) {
 | 
				
			||||||
 | 
						var res Response
 | 
				
			||||||
 | 
						err := codec.NewDecoderBytes(body, new(codec.JsonHandle)).Decode(&res)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, ErrInvalidJSON
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if header.Get("X-Etcd-Index") != "" {
 | 
				
			||||||
 | 
							res.Index, err = strconv.ParseUint(header.Get("X-Etcd-Index"), 10, 64)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res.ClusterID = header.Get("X-Etcd-Cluster-ID")
 | 
				
			||||||
 | 
						return &res, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unmarshalFailedKeysResponse(body []byte) error {
 | 
				
			||||||
 | 
						var etcdErr Error
 | 
				
			||||||
 | 
						if err := json.Unmarshal(body, &etcdErr); err != nil {
 | 
				
			||||||
 | 
							return ErrInvalidJSON
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return etcdErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										303
									
								
								vendor/github.com/coreos/etcd/client/members.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								vendor/github.com/coreos/etcd/client/members.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,303 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/coreos/etcd/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						defaultV2MembersPrefix = "/v2/members"
 | 
				
			||||||
 | 
						defaultLeaderSuffix    = "/leader"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Member struct {
 | 
				
			||||||
 | 
						// ID is the unique identifier of this Member.
 | 
				
			||||||
 | 
						ID string `json:"id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Name is a human-readable, non-unique identifier of this Member.
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PeerURLs represents the HTTP(S) endpoints this Member uses to
 | 
				
			||||||
 | 
						// participate in etcd's consensus protocol.
 | 
				
			||||||
 | 
						PeerURLs []string `json:"peerURLs"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ClientURLs represents the HTTP(S) endpoints on which this Member
 | 
				
			||||||
 | 
						// serves its client-facing APIs.
 | 
				
			||||||
 | 
						ClientURLs []string `json:"clientURLs"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type memberCollection []Member
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *memberCollection) UnmarshalJSON(data []byte) error {
 | 
				
			||||||
 | 
						d := struct {
 | 
				
			||||||
 | 
							Members []Member
 | 
				
			||||||
 | 
						}{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := json.Unmarshal(data, &d); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if d.Members == nil {
 | 
				
			||||||
 | 
							*c = make([]Member, 0)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*c = d.Members
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type memberCreateOrUpdateRequest struct {
 | 
				
			||||||
 | 
						PeerURLs types.URLs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *memberCreateOrUpdateRequest) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						s := struct {
 | 
				
			||||||
 | 
							PeerURLs []string `json:"peerURLs"`
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							PeerURLs: make([]string, len(m.PeerURLs)),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, u := range m.PeerURLs {
 | 
				
			||||||
 | 
							s.PeerURLs[i] = u.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return json.Marshal(&s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewMembersAPI constructs a new MembersAPI that uses HTTP to
 | 
				
			||||||
 | 
					// interact with etcd's membership API.
 | 
				
			||||||
 | 
					func NewMembersAPI(c Client) MembersAPI {
 | 
				
			||||||
 | 
						return &httpMembersAPI{
 | 
				
			||||||
 | 
							client: c,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MembersAPI interface {
 | 
				
			||||||
 | 
						// List enumerates the current cluster membership.
 | 
				
			||||||
 | 
						List(ctx context.Context) ([]Member, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add instructs etcd to accept a new Member into the cluster.
 | 
				
			||||||
 | 
						Add(ctx context.Context, peerURL string) (*Member, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Remove demotes an existing Member out of the cluster.
 | 
				
			||||||
 | 
						Remove(ctx context.Context, mID string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update instructs etcd to update an existing Member in the cluster.
 | 
				
			||||||
 | 
						Update(ctx context.Context, mID string, peerURLs []string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Leader gets current leader of the cluster
 | 
				
			||||||
 | 
						Leader(ctx context.Context) (*Member, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpMembersAPI struct {
 | 
				
			||||||
 | 
						client httpClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *httpMembersAPI) List(ctx context.Context) ([]Member, error) {
 | 
				
			||||||
 | 
						req := &membersAPIActionList{}
 | 
				
			||||||
 | 
						resp, body, err := m.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mCollection memberCollection
 | 
				
			||||||
 | 
						if err := json.Unmarshal(body, &mCollection); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return []Member(mCollection), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *httpMembersAPI) Add(ctx context.Context, peerURL string) (*Member, error) {
 | 
				
			||||||
 | 
						urls, err := types.NewURLs([]string{peerURL})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := &membersAPIActionAdd{peerURLs: urls}
 | 
				
			||||||
 | 
						resp, body, err := m.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := assertStatusCode(resp.StatusCode, http.StatusCreated, http.StatusConflict); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.StatusCode != http.StatusCreated {
 | 
				
			||||||
 | 
							var merr membersError
 | 
				
			||||||
 | 
							if err := json.Unmarshal(body, &merr); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, merr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var memb Member
 | 
				
			||||||
 | 
						if err := json.Unmarshal(body, &memb); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &memb, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *httpMembersAPI) Update(ctx context.Context, memberID string, peerURLs []string) error {
 | 
				
			||||||
 | 
						urls, err := types.NewURLs(peerURLs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := &membersAPIActionUpdate{peerURLs: urls, memberID: memberID}
 | 
				
			||||||
 | 
						resp, body, err := m.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusNotFound, http.StatusConflict); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.StatusCode != http.StatusNoContent {
 | 
				
			||||||
 | 
							var merr membersError
 | 
				
			||||||
 | 
							if err := json.Unmarshal(body, &merr); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return merr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *httpMembersAPI) Remove(ctx context.Context, memberID string) error {
 | 
				
			||||||
 | 
						req := &membersAPIActionRemove{memberID: memberID}
 | 
				
			||||||
 | 
						resp, _, err := m.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusGone)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *httpMembersAPI) Leader(ctx context.Context) (*Member, error) {
 | 
				
			||||||
 | 
						req := &membersAPIActionLeader{}
 | 
				
			||||||
 | 
						resp, body, err := m.client.Do(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var leader Member
 | 
				
			||||||
 | 
						if err := json.Unmarshal(body, &leader); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &leader, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type membersAPIActionList struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *membersAPIActionList) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2MembersURL(ep)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("GET", u.String(), nil)
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type membersAPIActionRemove struct {
 | 
				
			||||||
 | 
						memberID string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *membersAPIActionRemove) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2MembersURL(ep)
 | 
				
			||||||
 | 
						u.Path = path.Join(u.Path, d.memberID)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("DELETE", u.String(), nil)
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type membersAPIActionAdd struct {
 | 
				
			||||||
 | 
						peerURLs types.URLs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *membersAPIActionAdd) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2MembersURL(ep)
 | 
				
			||||||
 | 
						m := memberCreateOrUpdateRequest{PeerURLs: a.peerURLs}
 | 
				
			||||||
 | 
						b, _ := json.Marshal(&m)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("POST", u.String(), bytes.NewReader(b))
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type membersAPIActionUpdate struct {
 | 
				
			||||||
 | 
						memberID string
 | 
				
			||||||
 | 
						peerURLs types.URLs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *membersAPIActionUpdate) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2MembersURL(ep)
 | 
				
			||||||
 | 
						m := memberCreateOrUpdateRequest{PeerURLs: a.peerURLs}
 | 
				
			||||||
 | 
						u.Path = path.Join(u.Path, a.memberID)
 | 
				
			||||||
 | 
						b, _ := json.Marshal(&m)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("PUT", u.String(), bytes.NewReader(b))
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func assertStatusCode(got int, want ...int) (err error) {
 | 
				
			||||||
 | 
						for _, w := range want {
 | 
				
			||||||
 | 
							if w == got {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Errorf("unexpected status code %d", got)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type membersAPIActionLeader struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *membersAPIActionLeader) HTTPRequest(ep url.URL) *http.Request {
 | 
				
			||||||
 | 
						u := v2MembersURL(ep)
 | 
				
			||||||
 | 
						u.Path = path.Join(u.Path, defaultLeaderSuffix)
 | 
				
			||||||
 | 
						req, _ := http.NewRequest("GET", u.String(), nil)
 | 
				
			||||||
 | 
						return req
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// v2MembersURL add the necessary path to the provided endpoint
 | 
				
			||||||
 | 
					// to route requests to the default v2 members API.
 | 
				
			||||||
 | 
					func v2MembersURL(ep url.URL) *url.URL {
 | 
				
			||||||
 | 
						ep.Path = path.Join(ep.Path, defaultV2MembersPrefix)
 | 
				
			||||||
 | 
						return &ep
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type membersError struct {
 | 
				
			||||||
 | 
						Message string `json:"message"`
 | 
				
			||||||
 | 
						Code    int    `json:"-"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e membersError) Error() string {
 | 
				
			||||||
 | 
						return e.Message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								vendor/github.com/coreos/etcd/client/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								vendor/github.com/coreos/etcd/client/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					// Copyright 2016 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						roleNotFoundRegExp *regexp.Regexp
 | 
				
			||||||
 | 
						userNotFoundRegExp *regexp.Regexp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						roleNotFoundRegExp = regexp.MustCompile("auth: Role .* does not exist.")
 | 
				
			||||||
 | 
						userNotFoundRegExp = regexp.MustCompile("auth: User .* does not exist.")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsKeyNotFound returns true if the error code is ErrorCodeKeyNotFound.
 | 
				
			||||||
 | 
					func IsKeyNotFound(err error) bool {
 | 
				
			||||||
 | 
						if cErr, ok := err.(Error); ok {
 | 
				
			||||||
 | 
							return cErr.Code == ErrorCodeKeyNotFound
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsRoleNotFound returns true if the error means role not found of v2 API.
 | 
				
			||||||
 | 
					func IsRoleNotFound(err error) bool {
 | 
				
			||||||
 | 
						if ae, ok := err.(authError); ok {
 | 
				
			||||||
 | 
							return roleNotFoundRegExp.MatchString(ae.Message)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsUserNotFound returns true if the error means user not found of v2 API.
 | 
				
			||||||
 | 
					func IsUserNotFound(err error) bool {
 | 
				
			||||||
 | 
						if ae, ok := err.(authError); ok {
 | 
				
			||||||
 | 
							return userNotFoundRegExp.MatchString(ae.Message)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										202
									
								
								vendor/github.com/coreos/etcd/pkg/pathutil/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/coreos/etcd/pkg/pathutil/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   END OF TERMS AND CONDITIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   APPENDIX: How to apply the Apache License to your work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To apply the Apache License to your work, attach the following
 | 
				
			||||||
 | 
					      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
				
			||||||
 | 
					      replaced with your own identifying information. (Don't include
 | 
				
			||||||
 | 
					      the brackets!)  The text should be enclosed in the appropriate
 | 
				
			||||||
 | 
					      comment syntax for the file format. We also recommend that a
 | 
				
			||||||
 | 
					      file or class name and description of purpose be included on the
 | 
				
			||||||
 | 
					      same "printed page" as the copyright notice for easier
 | 
				
			||||||
 | 
					      identification within third-party archives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright [yyyy] [name of copyright owner]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										31
									
								
								vendor/github.com/coreos/etcd/pkg/pathutil/path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/coreos/etcd/pkg/pathutil/path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					// Copyright 2009 The Go Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package pathutil implements utility functions for handling slash-separated
 | 
				
			||||||
 | 
					// paths.
 | 
				
			||||||
 | 
					package pathutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CanonicalURLPath returns the canonical url path for p, which follows the rules:
 | 
				
			||||||
 | 
					// 1. the path always starts with "/"
 | 
				
			||||||
 | 
					// 2. replace multiple slashes with a single slash
 | 
				
			||||||
 | 
					// 3. replace each '.' '..' path name element with equivalent one
 | 
				
			||||||
 | 
					// 4. keep the trailing slash
 | 
				
			||||||
 | 
					// The function is borrowed from stdlib http.cleanPath in server.go.
 | 
				
			||||||
 | 
					func CanonicalURLPath(p string) string {
 | 
				
			||||||
 | 
						if p == "" {
 | 
				
			||||||
 | 
							return "/"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p[0] != '/' {
 | 
				
			||||||
 | 
							p = "/" + p
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						np := path.Clean(p)
 | 
				
			||||||
 | 
						// path.Clean removes trailing slash except for root,
 | 
				
			||||||
 | 
						// put the trailing slash back if necessary.
 | 
				
			||||||
 | 
						if p[len(p)-1] == '/' && np != "/" {
 | 
				
			||||||
 | 
							np += "/"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return np
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										202
									
								
								vendor/github.com/coreos/etcd/pkg/srv/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/coreos/etcd/pkg/srv/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   END OF TERMS AND CONDITIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   APPENDIX: How to apply the Apache License to your work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To apply the Apache License to your work, attach the following
 | 
				
			||||||
 | 
					      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
				
			||||||
 | 
					      replaced with your own identifying information. (Don't include
 | 
				
			||||||
 | 
					      the brackets!)  The text should be enclosed in the appropriate
 | 
				
			||||||
 | 
					      comment syntax for the file format. We also recommend that a
 | 
				
			||||||
 | 
					      file or class name and description of purpose be included on the
 | 
				
			||||||
 | 
					      same "printed page" as the copyright notice for easier
 | 
				
			||||||
 | 
					      identification within third-party archives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright [yyyy] [name of copyright owner]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										130
									
								
								vendor/github.com/coreos/etcd/pkg/srv/srv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								vendor/github.com/coreos/etcd/pkg/srv/srv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package srv looks up DNS SRV records.
 | 
				
			||||||
 | 
					package srv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/coreos/etcd/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// indirection for testing
 | 
				
			||||||
 | 
						lookupSRV      = net.LookupSRV // net.DefaultResolver.LookupSRV when ctxs don't conflict
 | 
				
			||||||
 | 
						resolveTCPAddr = net.ResolveTCPAddr
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetCluster gets the cluster information via DNS discovery.
 | 
				
			||||||
 | 
					// Also sees each entry as a separate instance.
 | 
				
			||||||
 | 
					func GetCluster(serviceScheme, service, name, dns string, apurls types.URLs) ([]string, error) {
 | 
				
			||||||
 | 
						tempName := int(0)
 | 
				
			||||||
 | 
						tcp2ap := make(map[string]url.URL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// First, resolve the apurls
 | 
				
			||||||
 | 
						for _, url := range apurls {
 | 
				
			||||||
 | 
							tcpAddr, err := resolveTCPAddr("tcp", url.Host)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tcp2ap[tcpAddr.String()] = url
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stringParts := []string{}
 | 
				
			||||||
 | 
						updateNodeMap := func(service, scheme string) error {
 | 
				
			||||||
 | 
							_, addrs, err := lookupSRV(service, "tcp", dns)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, srv := range addrs {
 | 
				
			||||||
 | 
								port := fmt.Sprintf("%d", srv.Port)
 | 
				
			||||||
 | 
								host := net.JoinHostPort(srv.Target, port)
 | 
				
			||||||
 | 
								tcpAddr, terr := resolveTCPAddr("tcp", host)
 | 
				
			||||||
 | 
								if terr != nil {
 | 
				
			||||||
 | 
									err = terr
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								n := ""
 | 
				
			||||||
 | 
								url, ok := tcp2ap[tcpAddr.String()]
 | 
				
			||||||
 | 
								if ok {
 | 
				
			||||||
 | 
									n = name
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if n == "" {
 | 
				
			||||||
 | 
									n = fmt.Sprintf("%d", tempName)
 | 
				
			||||||
 | 
									tempName++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// SRV records have a trailing dot but URL shouldn't.
 | 
				
			||||||
 | 
								shortHost := strings.TrimSuffix(srv.Target, ".")
 | 
				
			||||||
 | 
								urlHost := net.JoinHostPort(shortHost, port)
 | 
				
			||||||
 | 
								if ok && url.Scheme != scheme {
 | 
				
			||||||
 | 
									err = fmt.Errorf("bootstrap at %s from DNS for %s has scheme mismatch with expected peer %s", scheme+"://"+urlHost, service, url.String())
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									stringParts = append(stringParts, fmt.Sprintf("%s=%s://%s", n, scheme, urlHost))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(stringParts) == 0 {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := updateNodeMap(service, serviceScheme)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("error querying DNS SRV records for _%s %s", service, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return stringParts, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SRVClients struct {
 | 
				
			||||||
 | 
						Endpoints []string
 | 
				
			||||||
 | 
						SRVs      []*net.SRV
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetClient looks up the client endpoints for a service and domain.
 | 
				
			||||||
 | 
					func GetClient(service, domain string) (*SRVClients, error) {
 | 
				
			||||||
 | 
						var urls []*url.URL
 | 
				
			||||||
 | 
						var srvs []*net.SRV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateURLs := func(service, scheme string) error {
 | 
				
			||||||
 | 
							_, addrs, err := lookupSRV(service, "tcp", domain)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, srv := range addrs {
 | 
				
			||||||
 | 
								urls = append(urls, &url.URL{
 | 
				
			||||||
 | 
									Scheme: scheme,
 | 
				
			||||||
 | 
									Host:   net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							srvs = append(srvs, addrs...)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errHTTPS := updateURLs(service+"-ssl", "https")
 | 
				
			||||||
 | 
						errHTTP := updateURLs(service, "http")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if errHTTPS != nil && errHTTP != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						endpoints := make([]string, len(urls))
 | 
				
			||||||
 | 
						for i := range urls {
 | 
				
			||||||
 | 
							endpoints[i] = urls[i].String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &SRVClients{Endpoints: endpoints, SRVs: srvs}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										202
									
								
								vendor/github.com/coreos/etcd/pkg/types/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/coreos/etcd/pkg/types/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   END OF TERMS AND CONDITIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   APPENDIX: How to apply the Apache License to your work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To apply the Apache License to your work, attach the following
 | 
				
			||||||
 | 
					      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
				
			||||||
 | 
					      replaced with your own identifying information. (Don't include
 | 
				
			||||||
 | 
					      the brackets!)  The text should be enclosed in the appropriate
 | 
				
			||||||
 | 
					      comment syntax for the file format. We also recommend that a
 | 
				
			||||||
 | 
					      file or class name and description of purpose be included on the
 | 
				
			||||||
 | 
					      same "printed page" as the copyright notice for easier
 | 
				
			||||||
 | 
					      identification within third-party archives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright [yyyy] [name of copyright owner]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										17
									
								
								vendor/github.com/coreos/etcd/pkg/types/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/coreos/etcd/pkg/types/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package types declares various data types and implements type-checking
 | 
				
			||||||
 | 
					// functions.
 | 
				
			||||||
 | 
					package types
 | 
				
			||||||
							
								
								
									
										41
									
								
								vendor/github.com/coreos/etcd/pkg/types/id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/coreos/etcd/pkg/types/id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ID represents a generic identifier which is canonically
 | 
				
			||||||
 | 
					// stored as a uint64 but is typically represented as a
 | 
				
			||||||
 | 
					// base-16 string for input/output
 | 
				
			||||||
 | 
					type ID uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i ID) String() string {
 | 
				
			||||||
 | 
						return strconv.FormatUint(uint64(i), 16)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IDFromString attempts to create an ID from a base-16 string.
 | 
				
			||||||
 | 
					func IDFromString(s string) (ID, error) {
 | 
				
			||||||
 | 
						i, err := strconv.ParseUint(s, 16, 64)
 | 
				
			||||||
 | 
						return ID(i), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IDSlice implements the sort interface
 | 
				
			||||||
 | 
					type IDSlice []ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p IDSlice) Len() int           { return len(p) }
 | 
				
			||||||
 | 
					func (p IDSlice) Less(i, j int) bool { return uint64(p[i]) < uint64(p[j]) }
 | 
				
			||||||
 | 
					func (p IDSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 | 
				
			||||||
							
								
								
									
										178
									
								
								vendor/github.com/coreos/etcd/pkg/types/set.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								vendor/github.com/coreos/etcd/pkg/types/set.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Set interface {
 | 
				
			||||||
 | 
						Add(string)
 | 
				
			||||||
 | 
						Remove(string)
 | 
				
			||||||
 | 
						Contains(string) bool
 | 
				
			||||||
 | 
						Equals(Set) bool
 | 
				
			||||||
 | 
						Length() int
 | 
				
			||||||
 | 
						Values() []string
 | 
				
			||||||
 | 
						Copy() Set
 | 
				
			||||||
 | 
						Sub(Set) Set
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewUnsafeSet(values ...string) *unsafeSet {
 | 
				
			||||||
 | 
						set := &unsafeSet{make(map[string]struct{})}
 | 
				
			||||||
 | 
						for _, v := range values {
 | 
				
			||||||
 | 
							set.Add(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return set
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewThreadsafeSet(values ...string) *tsafeSet {
 | 
				
			||||||
 | 
						us := NewUnsafeSet(values...)
 | 
				
			||||||
 | 
						return &tsafeSet{us, sync.RWMutex{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type unsafeSet struct {
 | 
				
			||||||
 | 
						d map[string]struct{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add adds a new value to the set (no-op if the value is already present)
 | 
				
			||||||
 | 
					func (us *unsafeSet) Add(value string) {
 | 
				
			||||||
 | 
						us.d[value] = struct{}{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Remove removes the given value from the set
 | 
				
			||||||
 | 
					func (us *unsafeSet) Remove(value string) {
 | 
				
			||||||
 | 
						delete(us.d, value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Contains returns whether the set contains the given value
 | 
				
			||||||
 | 
					func (us *unsafeSet) Contains(value string) (exists bool) {
 | 
				
			||||||
 | 
						_, exists = us.d[value]
 | 
				
			||||||
 | 
						return exists
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainsAll returns whether the set contains all given values
 | 
				
			||||||
 | 
					func (us *unsafeSet) ContainsAll(values []string) bool {
 | 
				
			||||||
 | 
						for _, s := range values {
 | 
				
			||||||
 | 
							if !us.Contains(s) {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Equals returns whether the contents of two sets are identical
 | 
				
			||||||
 | 
					func (us *unsafeSet) Equals(other Set) bool {
 | 
				
			||||||
 | 
						v1 := sort.StringSlice(us.Values())
 | 
				
			||||||
 | 
						v2 := sort.StringSlice(other.Values())
 | 
				
			||||||
 | 
						v1.Sort()
 | 
				
			||||||
 | 
						v2.Sort()
 | 
				
			||||||
 | 
						return reflect.DeepEqual(v1, v2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Length returns the number of elements in the set
 | 
				
			||||||
 | 
					func (us *unsafeSet) Length() int {
 | 
				
			||||||
 | 
						return len(us.d)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Values returns the values of the Set in an unspecified order.
 | 
				
			||||||
 | 
					func (us *unsafeSet) Values() (values []string) {
 | 
				
			||||||
 | 
						values = make([]string, 0)
 | 
				
			||||||
 | 
						for val := range us.d {
 | 
				
			||||||
 | 
							values = append(values, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return values
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copy creates a new Set containing the values of the first
 | 
				
			||||||
 | 
					func (us *unsafeSet) Copy() Set {
 | 
				
			||||||
 | 
						cp := NewUnsafeSet()
 | 
				
			||||||
 | 
						for val := range us.d {
 | 
				
			||||||
 | 
							cp.Add(val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sub removes all elements in other from the set
 | 
				
			||||||
 | 
					func (us *unsafeSet) Sub(other Set) Set {
 | 
				
			||||||
 | 
						oValues := other.Values()
 | 
				
			||||||
 | 
						result := us.Copy().(*unsafeSet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, val := range oValues {
 | 
				
			||||||
 | 
							if _, ok := result.d[val]; !ok {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							delete(result.d, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type tsafeSet struct {
 | 
				
			||||||
 | 
						us *unsafeSet
 | 
				
			||||||
 | 
						m  sync.RWMutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Add(value string) {
 | 
				
			||||||
 | 
						ts.m.Lock()
 | 
				
			||||||
 | 
						defer ts.m.Unlock()
 | 
				
			||||||
 | 
						ts.us.Add(value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Remove(value string) {
 | 
				
			||||||
 | 
						ts.m.Lock()
 | 
				
			||||||
 | 
						defer ts.m.Unlock()
 | 
				
			||||||
 | 
						ts.us.Remove(value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Contains(value string) (exists bool) {
 | 
				
			||||||
 | 
						ts.m.RLock()
 | 
				
			||||||
 | 
						defer ts.m.RUnlock()
 | 
				
			||||||
 | 
						return ts.us.Contains(value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Equals(other Set) bool {
 | 
				
			||||||
 | 
						ts.m.RLock()
 | 
				
			||||||
 | 
						defer ts.m.RUnlock()
 | 
				
			||||||
 | 
						return ts.us.Equals(other)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Length() int {
 | 
				
			||||||
 | 
						ts.m.RLock()
 | 
				
			||||||
 | 
						defer ts.m.RUnlock()
 | 
				
			||||||
 | 
						return ts.us.Length()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Values() (values []string) {
 | 
				
			||||||
 | 
						ts.m.RLock()
 | 
				
			||||||
 | 
						defer ts.m.RUnlock()
 | 
				
			||||||
 | 
						return ts.us.Values()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Copy() Set {
 | 
				
			||||||
 | 
						ts.m.RLock()
 | 
				
			||||||
 | 
						defer ts.m.RUnlock()
 | 
				
			||||||
 | 
						usResult := ts.us.Copy().(*unsafeSet)
 | 
				
			||||||
 | 
						return &tsafeSet{usResult, sync.RWMutex{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tsafeSet) Sub(other Set) Set {
 | 
				
			||||||
 | 
						ts.m.RLock()
 | 
				
			||||||
 | 
						defer ts.m.RUnlock()
 | 
				
			||||||
 | 
						usResult := ts.us.Sub(other).(*unsafeSet)
 | 
				
			||||||
 | 
						return &tsafeSet{usResult, sync.RWMutex{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								vendor/github.com/coreos/etcd/pkg/types/slice.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/coreos/etcd/pkg/types/slice.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Uint64Slice implements sort interface
 | 
				
			||||||
 | 
					type Uint64Slice []uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p Uint64Slice) Len() int           { return len(p) }
 | 
				
			||||||
 | 
					func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
 | 
				
			||||||
 | 
					func (p Uint64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 | 
				
			||||||
							
								
								
									
										82
									
								
								vendor/github.com/coreos/etcd/pkg/types/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								vendor/github.com/coreos/etcd/pkg/types/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type URLs []url.URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewURLs(strs []string) (URLs, error) {
 | 
				
			||||||
 | 
						all := make([]url.URL, len(strs))
 | 
				
			||||||
 | 
						if len(all) == 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("no valid URLs given")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, in := range strs {
 | 
				
			||||||
 | 
							in = strings.TrimSpace(in)
 | 
				
			||||||
 | 
							u, err := url.Parse(in)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "unix" && u.Scheme != "unixs" {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("URL scheme must be http, https, unix, or unixs: %s", in)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, _, err := net.SplitHostPort(u.Host); err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if u.Path != "" {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("URL must not contain a path: %s", in)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							all[i] = *u
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						us := URLs(all)
 | 
				
			||||||
 | 
						us.Sort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return us, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MustNewURLs(strs []string) URLs {
 | 
				
			||||||
 | 
						urls, err := NewURLs(strs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return urls
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (us URLs) String() string {
 | 
				
			||||||
 | 
						return strings.Join(us.StringSlice(), ",")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (us *URLs) Sort() {
 | 
				
			||||||
 | 
						sort.Sort(us)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (us URLs) Len() int           { return len(us) }
 | 
				
			||||||
 | 
					func (us URLs) Less(i, j int) bool { return us[i].String() < us[j].String() }
 | 
				
			||||||
 | 
					func (us URLs) Swap(i, j int)      { us[i], us[j] = us[j], us[i] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (us URLs) StringSlice() []string {
 | 
				
			||||||
 | 
						out := make([]string, len(us))
 | 
				
			||||||
 | 
						for i := range us {
 | 
				
			||||||
 | 
							out[i] = us[i].String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										107
									
								
								vendor/github.com/coreos/etcd/pkg/types/urlsmap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/coreos/etcd/pkg/types/urlsmap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					// Copyright 2015 The etcd Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// URLsMap is a map from a name to its URLs.
 | 
				
			||||||
 | 
					type URLsMap map[string]URLs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewURLsMap returns a URLsMap instantiated from the given string,
 | 
				
			||||||
 | 
					// which consists of discovery-formatted names-to-URLs, like:
 | 
				
			||||||
 | 
					// mach0=http://1.1.1.1:2380,mach0=http://2.2.2.2::2380,mach1=http://3.3.3.3:2380,mach2=http://4.4.4.4:2380
 | 
				
			||||||
 | 
					func NewURLsMap(s string) (URLsMap, error) {
 | 
				
			||||||
 | 
						m := parse(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl := URLsMap{}
 | 
				
			||||||
 | 
						for name, urls := range m {
 | 
				
			||||||
 | 
							us, err := NewURLs(urls)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							cl[name] = us
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cl, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewURLsMapFromStringMap takes a map of strings and returns a URLsMap. The
 | 
				
			||||||
 | 
					// string values in the map can be multiple values separated by the sep string.
 | 
				
			||||||
 | 
					func NewURLsMapFromStringMap(m map[string]string, sep string) (URLsMap, error) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						um := URLsMap{}
 | 
				
			||||||
 | 
						for k, v := range m {
 | 
				
			||||||
 | 
							um[k], err = NewURLs(strings.Split(v, sep))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return um, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// String turns URLsMap into discovery-formatted name-to-URLs sorted by name.
 | 
				
			||||||
 | 
					func (c URLsMap) String() string {
 | 
				
			||||||
 | 
						var pairs []string
 | 
				
			||||||
 | 
						for name, urls := range c {
 | 
				
			||||||
 | 
							for _, url := range urls {
 | 
				
			||||||
 | 
								pairs = append(pairs, fmt.Sprintf("%s=%s", name, url.String()))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sort.Strings(pairs)
 | 
				
			||||||
 | 
						return strings.Join(pairs, ",")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// URLs returns a list of all URLs.
 | 
				
			||||||
 | 
					// The returned list is sorted in ascending lexicographical order.
 | 
				
			||||||
 | 
					func (c URLsMap) URLs() []string {
 | 
				
			||||||
 | 
						var urls []string
 | 
				
			||||||
 | 
						for _, us := range c {
 | 
				
			||||||
 | 
							for _, u := range us {
 | 
				
			||||||
 | 
								urls = append(urls, u.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sort.Strings(urls)
 | 
				
			||||||
 | 
						return urls
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Len returns the size of URLsMap.
 | 
				
			||||||
 | 
					func (c URLsMap) Len() int {
 | 
				
			||||||
 | 
						return len(c)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// parse parses the given string and returns a map listing the values specified for each key.
 | 
				
			||||||
 | 
					func parse(s string) map[string][]string {
 | 
				
			||||||
 | 
						m := make(map[string][]string)
 | 
				
			||||||
 | 
						for s != "" {
 | 
				
			||||||
 | 
							key := s
 | 
				
			||||||
 | 
							if i := strings.IndexAny(key, ","); i >= 0 {
 | 
				
			||||||
 | 
								key, s = key[:i], key[i+1:]
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								s = ""
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if key == "" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							value := ""
 | 
				
			||||||
 | 
							if i := strings.Index(key, "="); i >= 0 {
 | 
				
			||||||
 | 
								key, value = key[:i], key[i+1:]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							m[key] = append(m[key], value)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										202
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/coreos/go-semver/semver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/coreos/go-semver/semver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   END OF TERMS AND CONDITIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   APPENDIX: How to apply the Apache License to your work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To apply the Apache License to your work, attach the following
 | 
				
			||||||
 | 
					      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
				
			||||||
 | 
					      replaced with your own identifying information. (Don't include
 | 
				
			||||||
 | 
					      the brackets!)  The text should be enclosed in the appropriate
 | 
				
			||||||
 | 
					      comment syntax for the file format. We also recommend that a
 | 
				
			||||||
 | 
					      file or class name and description of purpose be included on the
 | 
				
			||||||
 | 
					      same "printed page" as the copyright notice for easier
 | 
				
			||||||
 | 
					      identification within third-party archives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright [yyyy] [name of copyright owner]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										268
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/coreos/go-semver/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/coreos/go-semver/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,268 @@
 | 
				
			|||||||
 | 
					// Copyright 2013-2015 CoreOS, Inc.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Semantic Versions http://semver.org
 | 
				
			||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Version struct {
 | 
				
			||||||
 | 
						Major      int64
 | 
				
			||||||
 | 
						Minor      int64
 | 
				
			||||||
 | 
						Patch      int64
 | 
				
			||||||
 | 
						PreRelease PreRelease
 | 
				
			||||||
 | 
						Metadata   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PreRelease string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func splitOff(input *string, delim string) (val string) {
 | 
				
			||||||
 | 
						parts := strings.SplitN(*input, delim, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(parts) == 2 {
 | 
				
			||||||
 | 
							*input = parts[0]
 | 
				
			||||||
 | 
							val = parts[1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return val
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(version string) *Version {
 | 
				
			||||||
 | 
						return Must(NewVersion(version))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewVersion(version string) (*Version, error) {
 | 
				
			||||||
 | 
						v := Version{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := v.Set(version); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &v, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Must is a helper for wrapping NewVersion and will panic if err is not nil.
 | 
				
			||||||
 | 
					func Must(v *Version, err error) *Version {
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set parses and updates v from the given version string. Implements flag.Value
 | 
				
			||||||
 | 
					func (v *Version) Set(version string) error {
 | 
				
			||||||
 | 
						metadata := splitOff(&version, "+")
 | 
				
			||||||
 | 
						preRelease := PreRelease(splitOff(&version, "-"))
 | 
				
			||||||
 | 
						dotParts := strings.SplitN(version, ".", 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(dotParts) != 3 {
 | 
				
			||||||
 | 
							return fmt.Errorf("%s is not in dotted-tri format", version)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parsed := make([]int64, 3, 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, v := range dotParts[:3] {
 | 
				
			||||||
 | 
							val, err := strconv.ParseInt(v, 10, 64)
 | 
				
			||||||
 | 
							parsed[i] = val
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v.Metadata = metadata
 | 
				
			||||||
 | 
						v.PreRelease = preRelease
 | 
				
			||||||
 | 
						v.Major = parsed[0]
 | 
				
			||||||
 | 
						v.Minor = parsed[1]
 | 
				
			||||||
 | 
						v.Patch = parsed[2]
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Version) String() string {
 | 
				
			||||||
 | 
						var buffer bytes.Buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Fprintf(&buffer, "%d.%d.%d", v.Major, v.Minor, v.Patch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if v.PreRelease != "" {
 | 
				
			||||||
 | 
							fmt.Fprintf(&buffer, "-%s", v.PreRelease)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if v.Metadata != "" {
 | 
				
			||||||
 | 
							fmt.Fprintf(&buffer, "+%s", v.Metadata)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return buffer.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
 | 
				
			||||||
 | 
						var data string
 | 
				
			||||||
 | 
						if err := unmarshal(&data); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v.Set(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Version) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						return []byte(`"` + v.String() + `"`), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v *Version) UnmarshalJSON(data []byte) error {
 | 
				
			||||||
 | 
						l := len(data)
 | 
				
			||||||
 | 
						if l == 0 || string(data) == `""` {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if l < 2 || data[0] != '"' || data[l-1] != '"' {
 | 
				
			||||||
 | 
							return errors.New("invalid semver string")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v.Set(string(data[1 : l-1]))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compare tests if v is less than, equal to, or greater than versionB,
 | 
				
			||||||
 | 
					// returning -1, 0, or +1 respectively.
 | 
				
			||||||
 | 
					func (v Version) Compare(versionB Version) int {
 | 
				
			||||||
 | 
						if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 {
 | 
				
			||||||
 | 
							return cmp
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return preReleaseCompare(v, versionB)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Equal tests if v is equal to versionB.
 | 
				
			||||||
 | 
					func (v Version) Equal(versionB Version) bool {
 | 
				
			||||||
 | 
						return v.Compare(versionB) == 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LessThan tests if v is less than versionB.
 | 
				
			||||||
 | 
					func (v Version) LessThan(versionB Version) bool {
 | 
				
			||||||
 | 
						return v.Compare(versionB) < 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Slice converts the comparable parts of the semver into a slice of integers.
 | 
				
			||||||
 | 
					func (v Version) Slice() []int64 {
 | 
				
			||||||
 | 
						return []int64{v.Major, v.Minor, v.Patch}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p PreRelease) Slice() []string {
 | 
				
			||||||
 | 
						preRelease := string(p)
 | 
				
			||||||
 | 
						return strings.Split(preRelease, ".")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func preReleaseCompare(versionA Version, versionB Version) int {
 | 
				
			||||||
 | 
						a := versionA.PreRelease
 | 
				
			||||||
 | 
						b := versionB.PreRelease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Handle the case where if two versions are otherwise equal it is the
 | 
				
			||||||
 | 
						 * one without a PreRelease that is greater */
 | 
				
			||||||
 | 
						if len(a) == 0 && (len(b) > 0) {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						} else if len(b) == 0 && (len(a) > 0) {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If there is a prerelease, check and compare each part.
 | 
				
			||||||
 | 
						return recursivePreReleaseCompare(a.Slice(), b.Slice())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func recursiveCompare(versionA []int64, versionB []int64) int {
 | 
				
			||||||
 | 
						if len(versionA) == 0 {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						a := versionA[0]
 | 
				
			||||||
 | 
						b := versionB[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if a > b {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						} else if a < b {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return recursiveCompare(versionA[1:], versionB[1:])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func recursivePreReleaseCompare(versionA []string, versionB []string) int {
 | 
				
			||||||
 | 
						// A larger set of pre-release fields has a higher precedence than a smaller set,
 | 
				
			||||||
 | 
						// if all of the preceding identifiers are equal.
 | 
				
			||||||
 | 
						if len(versionA) == 0 {
 | 
				
			||||||
 | 
							if len(versionB) > 0 {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						} else if len(versionB) == 0 {
 | 
				
			||||||
 | 
							// We're longer than versionB so return 1.
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						a := versionA[0]
 | 
				
			||||||
 | 
						b := versionB[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						aInt := false
 | 
				
			||||||
 | 
						bInt := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						aI, err := strconv.Atoi(versionA[0])
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							aInt = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bI, err := strconv.Atoi(versionB[0])
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							bInt = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle Integer Comparison
 | 
				
			||||||
 | 
						if aInt && bInt {
 | 
				
			||||||
 | 
							if aI > bI {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							} else if aI < bI {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle String Comparison
 | 
				
			||||||
 | 
						if a > b {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						} else if a < b {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return recursivePreReleaseCompare(versionA[1:], versionB[1:])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpMajor increments the Major field by 1 and resets all other fields to their default values
 | 
				
			||||||
 | 
					func (v *Version) BumpMajor() {
 | 
				
			||||||
 | 
						v.Major += 1
 | 
				
			||||||
 | 
						v.Minor = 0
 | 
				
			||||||
 | 
						v.Patch = 0
 | 
				
			||||||
 | 
						v.PreRelease = PreRelease("")
 | 
				
			||||||
 | 
						v.Metadata = ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpMinor increments the Minor field by 1 and resets all other fields to their default values
 | 
				
			||||||
 | 
					func (v *Version) BumpMinor() {
 | 
				
			||||||
 | 
						v.Minor += 1
 | 
				
			||||||
 | 
						v.Patch = 0
 | 
				
			||||||
 | 
						v.PreRelease = PreRelease("")
 | 
				
			||||||
 | 
						v.Metadata = ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpPatch increments the Patch field by 1 and resets all other fields to their default values
 | 
				
			||||||
 | 
					func (v *Version) BumpPatch() {
 | 
				
			||||||
 | 
						v.Patch += 1
 | 
				
			||||||
 | 
						v.PreRelease = PreRelease("")
 | 
				
			||||||
 | 
						v.Metadata = ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/coreos/go-semver/semver/sort.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/coreos/go-semver/semver/sort.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					// Copyright 2013-2015 CoreOS, Inc.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Versions []*Version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Versions) Len() int {
 | 
				
			||||||
 | 
						return len(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Versions) Swap(i, j int) {
 | 
				
			||||||
 | 
						s[i], s[j] = s[j], s[i]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Versions) Less(i, j int) bool {
 | 
				
			||||||
 | 
						return s[i].LessThan(*s[j])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sort sorts the given slice of Version
 | 
				
			||||||
 | 
					func Sort(versions []*Version) {
 | 
				
			||||||
 | 
						sort.Sort(Versions(versions))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										185
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/0doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/0doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,185 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					High Performance, Feature-Rich Idiomatic Go 1.4+ codec/encoding library for
 | 
				
			||||||
 | 
					binc, msgpack, cbor, json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Supported Serialization formats are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - msgpack: https://github.com/msgpack/msgpack
 | 
				
			||||||
 | 
					  - binc:    http://github.com/ugorji/binc
 | 
				
			||||||
 | 
					  - cbor:    http://cbor.io http://tools.ietf.org/html/rfc7049
 | 
				
			||||||
 | 
					  - json:    http://json.org http://tools.ietf.org/html/rfc7159
 | 
				
			||||||
 | 
					  - simple:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To install:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go get github.com/ugorji/go/codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This package will carefully use 'unsafe' for performance reasons in specific places.
 | 
				
			||||||
 | 
					You can build without unsafe use by passing the safe or appengine tag
 | 
				
			||||||
 | 
					i.e. 'go install -tags=safe ...'. Note that unsafe is only supported for the last 3
 | 
				
			||||||
 | 
					go sdk versions e.g. current go release is go 1.9, so we support unsafe use only from
 | 
				
			||||||
 | 
					go 1.7+ . This is because supporting unsafe requires knowledge of implementation details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For detailed usage information, read the primer at http://ugorji.net/blog/go-codec-primer .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The idiomatic Go support is as seen in other encoding packages in
 | 
				
			||||||
 | 
					the standard library (ie json, xml, gob, etc).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Rich Feature Set includes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Simple but extremely powerful and feature-rich API
 | 
				
			||||||
 | 
					  - Support for go1.4 and above, while selectively using newer APIs for later releases
 | 
				
			||||||
 | 
					  - Good code coverage ( > 70% )
 | 
				
			||||||
 | 
					  - Very High Performance.
 | 
				
			||||||
 | 
					    Our extensive benchmarks show us outperforming Gob, Json, Bson, etc by 2-4X.
 | 
				
			||||||
 | 
					  - Careful selected use of 'unsafe' for targeted performance gains.
 | 
				
			||||||
 | 
					    100% mode exists where 'unsafe' is not used at all.
 | 
				
			||||||
 | 
					  - Lock-free (sans mutex) concurrency for scaling to 100's of cores
 | 
				
			||||||
 | 
					  - Multiple conversions:
 | 
				
			||||||
 | 
					    Package coerces types where appropriate
 | 
				
			||||||
 | 
					    e.g. decode an int in the stream into a float, etc.
 | 
				
			||||||
 | 
					  - Corner Cases:
 | 
				
			||||||
 | 
					    Overflows, nil maps/slices, nil values in streams are handled correctly
 | 
				
			||||||
 | 
					  - Standard field renaming via tags
 | 
				
			||||||
 | 
					  - Support for omitting empty fields during an encoding
 | 
				
			||||||
 | 
					  - Encoding from any value and decoding into pointer to any value
 | 
				
			||||||
 | 
					    (struct, slice, map, primitives, pointers, interface{}, etc)
 | 
				
			||||||
 | 
					  - Extensions to support efficient encoding/decoding of any named types
 | 
				
			||||||
 | 
					  - Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
 | 
				
			||||||
 | 
					  - Decoding without a schema (into a interface{}).
 | 
				
			||||||
 | 
					    Includes Options to configure what specific map or slice type to use
 | 
				
			||||||
 | 
					    when decoding an encoded list or map into a nil interface{}
 | 
				
			||||||
 | 
					  - Encode a struct as an array, and decode struct from an array in the data stream
 | 
				
			||||||
 | 
					  - Comprehensive support for anonymous fields
 | 
				
			||||||
 | 
					  - Fast (no-reflection) encoding/decoding of common maps and slices
 | 
				
			||||||
 | 
					  - Code-generation for faster performance.
 | 
				
			||||||
 | 
					  - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
 | 
				
			||||||
 | 
					  - Support indefinite-length formats to enable true streaming
 | 
				
			||||||
 | 
					    (for formats which support it e.g. json, cbor)
 | 
				
			||||||
 | 
					  - Support canonical encoding, where a value is ALWAYS encoded as same sequence of bytes.
 | 
				
			||||||
 | 
					    This mostly applies to maps, where iteration order is non-deterministic.
 | 
				
			||||||
 | 
					  - NIL in data stream decoded as zero value
 | 
				
			||||||
 | 
					  - Never silently skip data when decoding.
 | 
				
			||||||
 | 
					    User decides whether to return an error or silently skip data when keys or indexes
 | 
				
			||||||
 | 
					    in the data stream do not map to fields in the struct.
 | 
				
			||||||
 | 
					  - Detect and error when encoding a cyclic reference (instead of stack overflow shutdown)
 | 
				
			||||||
 | 
					  - Encode/Decode from/to chan types (for iterative streaming support)
 | 
				
			||||||
 | 
					  - Drop-in replacement for encoding/json. `json:` key in struct tag supported.
 | 
				
			||||||
 | 
					  - Provides a RPC Server and Client Codec for net/rpc communication protocol.
 | 
				
			||||||
 | 
					  - Handle unique idiosyncrasies of codecs e.g.
 | 
				
			||||||
 | 
					    - For messagepack, configure how ambiguities in handling raw bytes are resolved
 | 
				
			||||||
 | 
					    - For messagepack, provide rpc server/client codec to support
 | 
				
			||||||
 | 
					      msgpack-rpc protocol defined at:
 | 
				
			||||||
 | 
					      https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Extension Support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Users can register a function to handle the encoding or decoding of
 | 
				
			||||||
 | 
					their custom types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are no restrictions on what the custom type can be. Some examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type BisSet   []int
 | 
				
			||||||
 | 
					    type BitSet64 uint64
 | 
				
			||||||
 | 
					    type UUID     string
 | 
				
			||||||
 | 
					    type MyStructWithUnexportedFields struct { a int; b bool; c []int; }
 | 
				
			||||||
 | 
					    type GifImage struct { ... }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As an illustration, MyStructWithUnexportedFields would normally be
 | 
				
			||||||
 | 
					encoded as an empty map because it has no exported fields, while UUID
 | 
				
			||||||
 | 
					would be encoded as a string. However, with extension support, you can
 | 
				
			||||||
 | 
					encode any of these however you like.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RPC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RPC Client and Server Codecs are implemented, so the codecs can be used
 | 
				
			||||||
 | 
					with the standard net/rpc package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Handle is SAFE for concurrent READ, but NOT SAFE for concurrent modification.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Encoder and Decoder are NOT safe for concurrent use.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Consequently, the usage model is basically:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Create and initialize the Handle before any use.
 | 
				
			||||||
 | 
					      Once created, DO NOT modify it.
 | 
				
			||||||
 | 
					    - Multiple Encoders or Decoders can now use the Handle concurrently.
 | 
				
			||||||
 | 
					      They only read information off the Handle (never write).
 | 
				
			||||||
 | 
					    - However, each Encoder or Decoder MUST not be used concurrently
 | 
				
			||||||
 | 
					    - To re-use an Encoder/Decoder, call Reset(...) on it first.
 | 
				
			||||||
 | 
					      This allows you use state maintained on the Encoder/Decoder.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sample usage model:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create and configure Handle
 | 
				
			||||||
 | 
					    var (
 | 
				
			||||||
 | 
					      bh codec.BincHandle
 | 
				
			||||||
 | 
					      mh codec.MsgpackHandle
 | 
				
			||||||
 | 
					      ch codec.CborHandle
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mh.MapType = reflect.TypeOf(map[string]interface{}(nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // configure extensions
 | 
				
			||||||
 | 
					    // e.g. for msgpack, define functions and enable Time support for tag 1
 | 
				
			||||||
 | 
					    // mh.SetExt(reflect.TypeOf(time.Time{}), 1, myExt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create and use decoder/encoder
 | 
				
			||||||
 | 
					    var (
 | 
				
			||||||
 | 
					      r io.Reader
 | 
				
			||||||
 | 
					      w io.Writer
 | 
				
			||||||
 | 
					      b []byte
 | 
				
			||||||
 | 
					      h = &bh // or mh to use msgpack
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dec = codec.NewDecoder(r, h)
 | 
				
			||||||
 | 
					    dec = codec.NewDecoderBytes(b, h)
 | 
				
			||||||
 | 
					    err = dec.Decode(&v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enc = codec.NewEncoder(w, h)
 | 
				
			||||||
 | 
					    enc = codec.NewEncoderBytes(&b, h)
 | 
				
			||||||
 | 
					    err = enc.Encode(v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //RPC Server
 | 
				
			||||||
 | 
					    go func() {
 | 
				
			||||||
 | 
					        for {
 | 
				
			||||||
 | 
					            conn, err := listener.Accept()
 | 
				
			||||||
 | 
					            rpcCodec := codec.GoRpc.ServerCodec(conn, h)
 | 
				
			||||||
 | 
					            //OR rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, h)
 | 
				
			||||||
 | 
					            rpc.ServeCodec(rpcCodec)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //RPC Communication (client side)
 | 
				
			||||||
 | 
					    conn, err = net.Dial("tcp", "localhost:5555")
 | 
				
			||||||
 | 
					    rpcCodec := codec.GoRpc.ClientCodec(conn, h)
 | 
				
			||||||
 | 
					    //OR rpcCodec := codec.MsgpackSpecRpc.ClientCodec(conn, h)
 | 
				
			||||||
 | 
					    client := rpc.NewClientWithCodec(rpcCodec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Running Tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To run tests, use the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To run the full suite of tests, use the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go test -tags alltests -run Suite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can run the tag 'safe' to run tests or build in safe mode. e.g.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go test -tags safe -run Json
 | 
				
			||||||
 | 
					    go test -tags "alltests safe" -run Suite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Running Benchmarks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Please see http://github.com/ugorji/go-codec-bench .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										202
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   END OF TERMS AND CONDITIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   APPENDIX: How to apply the Apache License to your work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To apply the Apache License to your work, attach the following
 | 
				
			||||||
 | 
					      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
				
			||||||
 | 
					      replaced with your own identifying information. (Don't include
 | 
				
			||||||
 | 
					      the brackets!)  The text should be enclosed in the appropriate
 | 
				
			||||||
 | 
					      comment syntax for the file format. We also recommend that a
 | 
				
			||||||
 | 
					      file or class name and description of purpose be included on the
 | 
				
			||||||
 | 
					      same "printed page" as the copyright notice for easier
 | 
				
			||||||
 | 
					      identification within third-party archives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright [yyyy] [name of copyright owner]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
							
								
								
									
										946
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/binc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										946
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/binc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,946 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const bincDoPrune = true // No longer needed. Needed before as C lib did not support pruning.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// vd as low 4 bits (there are 16 slots)
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						bincVdSpecial byte = iota
 | 
				
			||||||
 | 
						bincVdPosInt
 | 
				
			||||||
 | 
						bincVdNegInt
 | 
				
			||||||
 | 
						bincVdFloat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bincVdString
 | 
				
			||||||
 | 
						bincVdByteArray
 | 
				
			||||||
 | 
						bincVdArray
 | 
				
			||||||
 | 
						bincVdMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bincVdTimestamp
 | 
				
			||||||
 | 
						bincVdSmallInt
 | 
				
			||||||
 | 
						bincVdUnicodeOther
 | 
				
			||||||
 | 
						bincVdSymbol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bincVdDecimal
 | 
				
			||||||
 | 
						_               // open slot
 | 
				
			||||||
 | 
						_               // open slot
 | 
				
			||||||
 | 
						bincVdCustomExt = 0x0f
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						bincSpNil byte = iota
 | 
				
			||||||
 | 
						bincSpFalse
 | 
				
			||||||
 | 
						bincSpTrue
 | 
				
			||||||
 | 
						bincSpNan
 | 
				
			||||||
 | 
						bincSpPosInf
 | 
				
			||||||
 | 
						bincSpNegInf
 | 
				
			||||||
 | 
						bincSpZeroFloat
 | 
				
			||||||
 | 
						bincSpZero
 | 
				
			||||||
 | 
						bincSpNegOne
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						bincFlBin16 byte = iota
 | 
				
			||||||
 | 
						bincFlBin32
 | 
				
			||||||
 | 
						_ // bincFlBin32e
 | 
				
			||||||
 | 
						bincFlBin64
 | 
				
			||||||
 | 
						_ // bincFlBin64e
 | 
				
			||||||
 | 
						// others not currently supported
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type bincEncDriver struct {
 | 
				
			||||||
 | 
						e *Encoder
 | 
				
			||||||
 | 
						w encWriter
 | 
				
			||||||
 | 
						m map[string]uint16 // symbols
 | 
				
			||||||
 | 
						b [scratchByteArrayLen]byte
 | 
				
			||||||
 | 
						s uint16 // symbols sequencer
 | 
				
			||||||
 | 
						// encNoSeparator
 | 
				
			||||||
 | 
						encDriverNoopContainerWriter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) IsBuiltinType(rt uintptr) bool {
 | 
				
			||||||
 | 
						return rt == timeTypId
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {
 | 
				
			||||||
 | 
						if rt == timeTypId {
 | 
				
			||||||
 | 
							var bs []byte
 | 
				
			||||||
 | 
							switch x := v.(type) {
 | 
				
			||||||
 | 
							case time.Time:
 | 
				
			||||||
 | 
								bs = encodeTime(x)
 | 
				
			||||||
 | 
							case *time.Time:
 | 
				
			||||||
 | 
								bs = encodeTime(*x)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								e.e.errorf("binc error encoding builtin: expect time.Time, received %T", v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							e.w.writen1(bincVdTimestamp<<4 | uint8(len(bs)))
 | 
				
			||||||
 | 
							e.w.writeb(bs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeNil() {
 | 
				
			||||||
 | 
						e.w.writen1(bincVdSpecial<<4 | bincSpNil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeBool(b bool) {
 | 
				
			||||||
 | 
						if b {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSpecial<<4 | bincSpTrue)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSpecial<<4 | bincSpFalse)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeFloat32(f float32) {
 | 
				
			||||||
 | 
						if f == 0 {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSpecial<<4 | bincSpZeroFloat)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.w.writen1(bincVdFloat<<4 | bincFlBin32)
 | 
				
			||||||
 | 
						bigenHelper{e.b[:4], e.w}.writeUint32(math.Float32bits(f))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeFloat64(f float64) {
 | 
				
			||||||
 | 
						if f == 0 {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSpecial<<4 | bincSpZeroFloat)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bigen.PutUint64(e.b[:8], math.Float64bits(f))
 | 
				
			||||||
 | 
						if bincDoPrune {
 | 
				
			||||||
 | 
							i := 7
 | 
				
			||||||
 | 
							for ; i >= 0 && (e.b[i] == 0); i-- {
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							i++
 | 
				
			||||||
 | 
							if i <= 6 {
 | 
				
			||||||
 | 
								e.w.writen1(bincVdFloat<<4 | 0x8 | bincFlBin64)
 | 
				
			||||||
 | 
								e.w.writen1(byte(i))
 | 
				
			||||||
 | 
								e.w.writeb(e.b[:i])
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.w.writen1(bincVdFloat<<4 | bincFlBin64)
 | 
				
			||||||
 | 
						e.w.writeb(e.b[:8])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) encIntegerPrune(bd byte, pos bool, v uint64, lim uint8) {
 | 
				
			||||||
 | 
						if lim == 4 {
 | 
				
			||||||
 | 
							bigen.PutUint32(e.b[:lim], uint32(v))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bigen.PutUint64(e.b[:lim], v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if bincDoPrune {
 | 
				
			||||||
 | 
							i := pruneSignExt(e.b[:lim], pos)
 | 
				
			||||||
 | 
							e.w.writen1(bd | lim - 1 - byte(i))
 | 
				
			||||||
 | 
							e.w.writeb(e.b[i:lim])
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.w.writen1(bd | lim - 1)
 | 
				
			||||||
 | 
							e.w.writeb(e.b[:lim])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeInt(v int64) {
 | 
				
			||||||
 | 
						const nbd byte = bincVdNegInt << 4
 | 
				
			||||||
 | 
						if v >= 0 {
 | 
				
			||||||
 | 
							e.encUint(bincVdPosInt<<4, true, uint64(v))
 | 
				
			||||||
 | 
						} else if v == -1 {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSpecial<<4 | bincSpNegOne)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encUint(bincVdNegInt<<4, false, uint64(-v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeUint(v uint64) {
 | 
				
			||||||
 | 
						e.encUint(bincVdPosInt<<4, true, v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) encUint(bd byte, pos bool, v uint64) {
 | 
				
			||||||
 | 
						if v == 0 {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSpecial<<4 | bincSpZero)
 | 
				
			||||||
 | 
						} else if pos && v >= 1 && v <= 16 {
 | 
				
			||||||
 | 
							e.w.writen1(bincVdSmallInt<<4 | byte(v-1))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint8 {
 | 
				
			||||||
 | 
							e.w.writen2(bd|0x0, byte(v))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint16 {
 | 
				
			||||||
 | 
							e.w.writen1(bd | 0x01)
 | 
				
			||||||
 | 
							bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint32 {
 | 
				
			||||||
 | 
							e.encIntegerPrune(bd, pos, v, 4)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encIntegerPrune(bd, pos, v, 8)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, _ *Encoder) {
 | 
				
			||||||
 | 
						bs := ext.WriteExt(rv)
 | 
				
			||||||
 | 
						if bs == nil {
 | 
				
			||||||
 | 
							e.EncodeNil()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.encodeExtPreamble(uint8(xtag), len(bs))
 | 
				
			||||||
 | 
						e.w.writeb(bs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeRawExt(re *RawExt, _ *Encoder) {
 | 
				
			||||||
 | 
						e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
 | 
				
			||||||
 | 
						e.w.writeb(re.Data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) encodeExtPreamble(xtag byte, length int) {
 | 
				
			||||||
 | 
						e.encLen(bincVdCustomExt<<4, uint64(length))
 | 
				
			||||||
 | 
						e.w.writen1(xtag)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) WriteArrayStart(length int) {
 | 
				
			||||||
 | 
						e.encLen(bincVdArray<<4, uint64(length))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) WriteMapStart(length int) {
 | 
				
			||||||
 | 
						e.encLen(bincVdMap<<4, uint64(length))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeString(c charEncoding, v string) {
 | 
				
			||||||
 | 
						l := uint64(len(v))
 | 
				
			||||||
 | 
						e.encBytesLen(c, l)
 | 
				
			||||||
 | 
						if l > 0 {
 | 
				
			||||||
 | 
							e.w.writestr(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeSymbol(v string) {
 | 
				
			||||||
 | 
						// if WriteSymbolsNoRefs {
 | 
				
			||||||
 | 
						// 	e.encodeString(c_UTF8, v)
 | 
				
			||||||
 | 
						// 	return
 | 
				
			||||||
 | 
						// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//symbols only offer benefit when string length > 1.
 | 
				
			||||||
 | 
						//This is because strings with length 1 take only 2 bytes to store
 | 
				
			||||||
 | 
						//(bd with embedded length, and single byte for string val).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l := len(v)
 | 
				
			||||||
 | 
						if l == 0 {
 | 
				
			||||||
 | 
							e.encBytesLen(c_UTF8, 0)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						} else if l == 1 {
 | 
				
			||||||
 | 
							e.encBytesLen(c_UTF8, 1)
 | 
				
			||||||
 | 
							e.w.writen1(v[0])
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if e.m == nil {
 | 
				
			||||||
 | 
							e.m = make(map[string]uint16, 16)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ui, ok := e.m[v]
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							if ui <= math.MaxUint8 {
 | 
				
			||||||
 | 
								e.w.writen2(bincVdSymbol<<4, byte(ui))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								e.w.writen1(bincVdSymbol<<4 | 0x8)
 | 
				
			||||||
 | 
								bigenHelper{e.b[:2], e.w}.writeUint16(ui)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.s++
 | 
				
			||||||
 | 
							ui = e.s
 | 
				
			||||||
 | 
							//ui = uint16(atomic.AddUint32(&e.s, 1))
 | 
				
			||||||
 | 
							e.m[v] = ui
 | 
				
			||||||
 | 
							var lenprec uint8
 | 
				
			||||||
 | 
							if l <= math.MaxUint8 {
 | 
				
			||||||
 | 
								// lenprec = 0
 | 
				
			||||||
 | 
							} else if l <= math.MaxUint16 {
 | 
				
			||||||
 | 
								lenprec = 1
 | 
				
			||||||
 | 
							} else if int64(l) <= math.MaxUint32 {
 | 
				
			||||||
 | 
								lenprec = 2
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								lenprec = 3
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if ui <= math.MaxUint8 {
 | 
				
			||||||
 | 
								e.w.writen2(bincVdSymbol<<4|0x0|0x4|lenprec, byte(ui))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								e.w.writen1(bincVdSymbol<<4 | 0x8 | 0x4 | lenprec)
 | 
				
			||||||
 | 
								bigenHelper{e.b[:2], e.w}.writeUint16(ui)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if lenprec == 0 {
 | 
				
			||||||
 | 
								e.w.writen1(byte(l))
 | 
				
			||||||
 | 
							} else if lenprec == 1 {
 | 
				
			||||||
 | 
								bigenHelper{e.b[:2], e.w}.writeUint16(uint16(l))
 | 
				
			||||||
 | 
							} else if lenprec == 2 {
 | 
				
			||||||
 | 
								bigenHelper{e.b[:4], e.w}.writeUint32(uint32(l))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								bigenHelper{e.b[:8], e.w}.writeUint64(uint64(l))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							e.w.writestr(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
 | 
				
			||||||
 | 
						l := uint64(len(v))
 | 
				
			||||||
 | 
						e.encBytesLen(c, l)
 | 
				
			||||||
 | 
						if l > 0 {
 | 
				
			||||||
 | 
							e.w.writeb(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) encBytesLen(c charEncoding, length uint64) {
 | 
				
			||||||
 | 
						//TODO: support bincUnicodeOther (for now, just use string or bytearray)
 | 
				
			||||||
 | 
						if c == c_RAW {
 | 
				
			||||||
 | 
							e.encLen(bincVdByteArray<<4, length)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encLen(bincVdString<<4, length)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) encLen(bd byte, l uint64) {
 | 
				
			||||||
 | 
						if l < 12 {
 | 
				
			||||||
 | 
							e.w.writen1(bd | uint8(l+4))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encLenNumber(bd, l)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) encLenNumber(bd byte, v uint64) {
 | 
				
			||||||
 | 
						if v <= math.MaxUint8 {
 | 
				
			||||||
 | 
							e.w.writen2(bd, byte(v))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint16 {
 | 
				
			||||||
 | 
							e.w.writen1(bd | 0x01)
 | 
				
			||||||
 | 
							bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint32 {
 | 
				
			||||||
 | 
							e.w.writen1(bd | 0x02)
 | 
				
			||||||
 | 
							bigenHelper{e.b[:4], e.w}.writeUint32(uint32(v))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.w.writen1(bd | 0x03)
 | 
				
			||||||
 | 
							bigenHelper{e.b[:8], e.w}.writeUint64(uint64(v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type bincDecSymbol struct {
 | 
				
			||||||
 | 
						s string
 | 
				
			||||||
 | 
						b []byte
 | 
				
			||||||
 | 
						i uint16
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type bincDecDriver struct {
 | 
				
			||||||
 | 
						d      *Decoder
 | 
				
			||||||
 | 
						h      *BincHandle
 | 
				
			||||||
 | 
						r      decReader
 | 
				
			||||||
 | 
						br     bool // bytes reader
 | 
				
			||||||
 | 
						bdRead bool
 | 
				
			||||||
 | 
						bd     byte
 | 
				
			||||||
 | 
						vd     byte
 | 
				
			||||||
 | 
						vs     byte
 | 
				
			||||||
 | 
						// noStreamingCodec
 | 
				
			||||||
 | 
						// decNoSeparator
 | 
				
			||||||
 | 
						b [scratchByteArrayLen]byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// linear searching on this slice is ok,
 | 
				
			||||||
 | 
						// because we typically expect < 32 symbols in each stream.
 | 
				
			||||||
 | 
						s []bincDecSymbol
 | 
				
			||||||
 | 
						decDriverNoopContainerReader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) readNextBd() {
 | 
				
			||||||
 | 
						d.bd = d.r.readn1()
 | 
				
			||||||
 | 
						d.vd = d.bd >> 4
 | 
				
			||||||
 | 
						d.vs = d.bd & 0x0f
 | 
				
			||||||
 | 
						d.bdRead = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) uncacheRead() {
 | 
				
			||||||
 | 
						if d.bdRead {
 | 
				
			||||||
 | 
							d.r.unreadn1()
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) ContainerType() (vt valueType) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.vd == bincVdSpecial && d.vs == bincSpNil {
 | 
				
			||||||
 | 
							return valueTypeNil
 | 
				
			||||||
 | 
						} else if d.vd == bincVdByteArray {
 | 
				
			||||||
 | 
							return valueTypeBytes
 | 
				
			||||||
 | 
						} else if d.vd == bincVdString {
 | 
				
			||||||
 | 
							return valueTypeString
 | 
				
			||||||
 | 
						} else if d.vd == bincVdArray {
 | 
				
			||||||
 | 
							return valueTypeArray
 | 
				
			||||||
 | 
						} else if d.vd == bincVdMap {
 | 
				
			||||||
 | 
							return valueTypeMap
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return valueTypeUnset
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) TryDecodeAsNil() bool {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == bincVdSpecial<<4|bincSpNil {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) IsBuiltinType(rt uintptr) bool {
 | 
				
			||||||
 | 
						return rt == timeTypId
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeBuiltin(rt uintptr, v interface{}) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rt == timeTypId {
 | 
				
			||||||
 | 
							if d.vd != bincVdTimestamp {
 | 
				
			||||||
 | 
								d.d.errorf("Invalid d.vd. Expecting 0x%x. Received: 0x%x", bincVdTimestamp, d.vd)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tt, err := decodeTime(d.r.readx(int(d.vs)))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							var vt *time.Time = v.(*time.Time)
 | 
				
			||||||
 | 
							*vt = tt
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decFloatPre(vs, defaultLen byte) {
 | 
				
			||||||
 | 
						if vs&0x8 == 0 {
 | 
				
			||||||
 | 
							d.r.readb(d.b[0:defaultLen])
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							l := d.r.readn1()
 | 
				
			||||||
 | 
							if l > 8 {
 | 
				
			||||||
 | 
								d.d.errorf("At most 8 bytes used to represent float. Received: %v bytes", l)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for i := l; i < 8; i++ {
 | 
				
			||||||
 | 
								d.b[i] = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							d.r.readb(d.b[0:l])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decFloat() (f float64) {
 | 
				
			||||||
 | 
						//if true { f = math.Float64frombits(bigen.Uint64(d.r.readx(8))); break; }
 | 
				
			||||||
 | 
						if x := d.vs & 0x7; x == bincFlBin32 {
 | 
				
			||||||
 | 
							d.decFloatPre(d.vs, 4)
 | 
				
			||||||
 | 
							f = float64(math.Float32frombits(bigen.Uint32(d.b[0:4])))
 | 
				
			||||||
 | 
						} else if x == bincFlBin64 {
 | 
				
			||||||
 | 
							d.decFloatPre(d.vs, 8)
 | 
				
			||||||
 | 
							f = math.Float64frombits(bigen.Uint64(d.b[0:8]))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("only float32 and float64 are supported. d.vd: 0x%x, d.vs: 0x%x", d.vd, d.vs)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decUint() (v uint64) {
 | 
				
			||||||
 | 
						// need to inline the code (interface conversion and type assertion expensive)
 | 
				
			||||||
 | 
						switch d.vs {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							v = uint64(d.r.readn1())
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							d.r.readb(d.b[6:8])
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint16(d.b[6:8]))
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							d.b[4] = 0
 | 
				
			||||||
 | 
							d.r.readb(d.b[5:8])
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint32(d.b[4:8]))
 | 
				
			||||||
 | 
						case 3:
 | 
				
			||||||
 | 
							d.r.readb(d.b[4:8])
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint32(d.b[4:8]))
 | 
				
			||||||
 | 
						case 4, 5, 6:
 | 
				
			||||||
 | 
							lim := int(7 - d.vs)
 | 
				
			||||||
 | 
							d.r.readb(d.b[lim:8])
 | 
				
			||||||
 | 
							for i := 0; i < lim; i++ {
 | 
				
			||||||
 | 
								d.b[i] = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint64(d.b[:8]))
 | 
				
			||||||
 | 
						case 7:
 | 
				
			||||||
 | 
							d.r.readb(d.b[:8])
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint64(d.b[:8]))
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							d.d.errorf("unsigned integers with greater than 64 bits of precision not supported")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decCheckInteger() (ui uint64, neg bool) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vd, vs := d.vd, d.vs
 | 
				
			||||||
 | 
						if vd == bincVdPosInt {
 | 
				
			||||||
 | 
							ui = d.decUint()
 | 
				
			||||||
 | 
						} else if vd == bincVdNegInt {
 | 
				
			||||||
 | 
							ui = d.decUint()
 | 
				
			||||||
 | 
							neg = true
 | 
				
			||||||
 | 
						} else if vd == bincVdSmallInt {
 | 
				
			||||||
 | 
							ui = uint64(d.vs) + 1
 | 
				
			||||||
 | 
						} else if vd == bincVdSpecial {
 | 
				
			||||||
 | 
							if vs == bincSpZero {
 | 
				
			||||||
 | 
								//i = 0
 | 
				
			||||||
 | 
							} else if vs == bincSpNegOne {
 | 
				
			||||||
 | 
								neg = true
 | 
				
			||||||
 | 
								ui = 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								d.d.errorf("numeric decode fails for special value: d.vs: 0x%x", d.vs)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("number can only be decoded from uint or int values. d.bd: 0x%x, d.vd: 0x%x", d.bd, d.vd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeInt(bitsize uint8) (i int64) {
 | 
				
			||||||
 | 
						ui, neg := d.decCheckInteger()
 | 
				
			||||||
 | 
						i, overflow := chkOvf.SignedInt(ui)
 | 
				
			||||||
 | 
						if overflow {
 | 
				
			||||||
 | 
							d.d.errorf("simple: overflow converting %v to signed integer", ui)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if neg {
 | 
				
			||||||
 | 
							i = -i
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if chkOvf.Int(i, bitsize) {
 | 
				
			||||||
 | 
							d.d.errorf("binc: overflow integer: %v for num bits: %v", i, bitsize)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
 | 
				
			||||||
 | 
						ui, neg := d.decCheckInteger()
 | 
				
			||||||
 | 
						if neg {
 | 
				
			||||||
 | 
							d.d.errorf("Assigning negative signed value to unsigned type")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if chkOvf.Uint(ui, bitsize) {
 | 
				
			||||||
 | 
							d.d.errorf("binc: overflow integer: %v", ui)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vd, vs := d.vd, d.vs
 | 
				
			||||||
 | 
						if vd == bincVdSpecial {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							if vs == bincSpNan {
 | 
				
			||||||
 | 
								return math.NaN()
 | 
				
			||||||
 | 
							} else if vs == bincSpPosInf {
 | 
				
			||||||
 | 
								return math.Inf(1)
 | 
				
			||||||
 | 
							} else if vs == bincSpZeroFloat || vs == bincSpZero {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							} else if vs == bincSpNegInf {
 | 
				
			||||||
 | 
								return math.Inf(-1)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								d.d.errorf("Invalid d.vs decoding float where d.vd=bincVdSpecial: %v", d.vs)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if vd == bincVdFloat {
 | 
				
			||||||
 | 
							f = d.decFloat()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							f = float64(d.DecodeInt(64))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if chkOverflow32 && chkOvf.Float32(f) {
 | 
				
			||||||
 | 
							d.d.errorf("binc: float32 overflow: %v", f)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// bool can be decoded from bool only (single byte).
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeBool() (b bool) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if bd := d.bd; bd == (bincVdSpecial | bincSpFalse) {
 | 
				
			||||||
 | 
							// b = false
 | 
				
			||||||
 | 
						} else if bd == (bincVdSpecial | bincSpTrue) {
 | 
				
			||||||
 | 
							b = true
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) ReadMapStart() (length int) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.vd != bincVdMap {
 | 
				
			||||||
 | 
							d.d.errorf("Invalid d.vd for map. Expecting 0x%x. Got: 0x%x", bincVdMap, d.vd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						length = d.decLen()
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) ReadArrayStart() (length int) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.vd != bincVdArray {
 | 
				
			||||||
 | 
							d.d.errorf("Invalid d.vd for array. Expecting 0x%x. Got: 0x%x", bincVdArray, d.vd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						length = d.decLen()
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decLen() int {
 | 
				
			||||||
 | 
						if d.vs > 3 {
 | 
				
			||||||
 | 
							return int(d.vs - 4)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return int(d.decLenNumber())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decLenNumber() (v uint64) {
 | 
				
			||||||
 | 
						if x := d.vs; x == 0 {
 | 
				
			||||||
 | 
							v = uint64(d.r.readn1())
 | 
				
			||||||
 | 
						} else if x == 1 {
 | 
				
			||||||
 | 
							d.r.readb(d.b[6:8])
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint16(d.b[6:8]))
 | 
				
			||||||
 | 
						} else if x == 2 {
 | 
				
			||||||
 | 
							d.r.readb(d.b[4:8])
 | 
				
			||||||
 | 
							v = uint64(bigen.Uint32(d.b[4:8]))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.r.readb(d.b[:8])
 | 
				
			||||||
 | 
							v = bigen.Uint64(d.b[:8])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decStringAndBytes(bs []byte, withString, zerocopy bool) (bs2 []byte, s string) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == bincVdSpecial<<4|bincSpNil {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var slen int = -1
 | 
				
			||||||
 | 
						// var ok bool
 | 
				
			||||||
 | 
						switch d.vd {
 | 
				
			||||||
 | 
						case bincVdString, bincVdByteArray:
 | 
				
			||||||
 | 
							slen = d.decLen()
 | 
				
			||||||
 | 
							if zerocopy {
 | 
				
			||||||
 | 
								if d.br {
 | 
				
			||||||
 | 
									bs2 = d.r.readx(slen)
 | 
				
			||||||
 | 
								} else if len(bs) == 0 {
 | 
				
			||||||
 | 
									bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, d.b[:])
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, bs)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, bs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if withString {
 | 
				
			||||||
 | 
								s = string(bs2)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case bincVdSymbol:
 | 
				
			||||||
 | 
							// zerocopy doesn't apply for symbols,
 | 
				
			||||||
 | 
							// as the values must be stored in a table for later use.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							//from vs: extract numSymbolBytes, containsStringVal, strLenPrecision,
 | 
				
			||||||
 | 
							//extract symbol
 | 
				
			||||||
 | 
							//if containsStringVal, read it and put in map
 | 
				
			||||||
 | 
							//else look in map for string value
 | 
				
			||||||
 | 
							var symbol uint16
 | 
				
			||||||
 | 
							vs := d.vs
 | 
				
			||||||
 | 
							if vs&0x8 == 0 {
 | 
				
			||||||
 | 
								symbol = uint16(d.r.readn1())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								symbol = uint16(bigen.Uint16(d.r.readx(2)))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if d.s == nil {
 | 
				
			||||||
 | 
								d.s = make([]bincDecSymbol, 0, 16)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if vs&0x4 == 0 {
 | 
				
			||||||
 | 
								for i := range d.s {
 | 
				
			||||||
 | 
									j := &d.s[i]
 | 
				
			||||||
 | 
									if j.i == symbol {
 | 
				
			||||||
 | 
										bs2 = j.b
 | 
				
			||||||
 | 
										if withString {
 | 
				
			||||||
 | 
											if j.s == "" && bs2 != nil {
 | 
				
			||||||
 | 
												j.s = string(bs2)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											s = j.s
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								switch vs & 0x3 {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									slen = int(d.r.readn1())
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									slen = int(bigen.Uint16(d.r.readx(2)))
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									slen = int(bigen.Uint32(d.r.readx(4)))
 | 
				
			||||||
 | 
								case 3:
 | 
				
			||||||
 | 
									slen = int(bigen.Uint64(d.r.readx(8)))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// since using symbols, do not store any part of
 | 
				
			||||||
 | 
								// the parameter bs in the map, as it might be a shared buffer.
 | 
				
			||||||
 | 
								// bs2 = decByteSlice(d.r, slen, bs)
 | 
				
			||||||
 | 
								bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, nil)
 | 
				
			||||||
 | 
								if withString {
 | 
				
			||||||
 | 
									s = string(bs2)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d.s = append(d.s, bincDecSymbol{i: symbol, s: s, b: bs2})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							d.d.errorf("Invalid d.vd. Expecting string:0x%x, bytearray:0x%x or symbol: 0x%x. Got: 0x%x",
 | 
				
			||||||
 | 
								bincVdString, bincVdByteArray, bincVdSymbol, d.vd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeString() (s string) {
 | 
				
			||||||
 | 
						// DecodeBytes does not accommodate symbols, whose impl stores string version in map.
 | 
				
			||||||
 | 
						// Use decStringAndBytes directly.
 | 
				
			||||||
 | 
						// return string(d.DecodeBytes(d.b[:], true, true))
 | 
				
			||||||
 | 
						_, s = d.decStringAndBytes(d.b[:], true, true)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeStringAsBytes() (s []byte) {
 | 
				
			||||||
 | 
						s, _ = d.decStringAndBytes(d.b[:], false, true)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == bincVdSpecial<<4|bincSpNil {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var clen int
 | 
				
			||||||
 | 
						if d.vd == bincVdString || d.vd == bincVdByteArray {
 | 
				
			||||||
 | 
							clen = d.decLen()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("Invalid d.vd for bytes. Expecting string:0x%x or bytearray:0x%x. Got: 0x%x",
 | 
				
			||||||
 | 
								bincVdString, bincVdByteArray, d.vd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						if zerocopy {
 | 
				
			||||||
 | 
							if d.br {
 | 
				
			||||||
 | 
								return d.r.readx(clen)
 | 
				
			||||||
 | 
							} else if len(bs) == 0 {
 | 
				
			||||||
 | 
								bs = d.b[:]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return decByteSlice(d.r, clen, d.d.h.MaxInitLen, bs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
 | 
				
			||||||
 | 
						if xtag > 0xff {
 | 
				
			||||||
 | 
							d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
 | 
				
			||||||
 | 
						realxtag = uint64(realxtag1)
 | 
				
			||||||
 | 
						if ext == nil {
 | 
				
			||||||
 | 
							re := rv.(*RawExt)
 | 
				
			||||||
 | 
							re.Tag = realxtag
 | 
				
			||||||
 | 
							re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							ext.ReadExt(rv, xbs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.vd == bincVdCustomExt {
 | 
				
			||||||
 | 
							l := d.decLen()
 | 
				
			||||||
 | 
							xtag = d.r.readn1()
 | 
				
			||||||
 | 
							if verifyTag && xtag != tag {
 | 
				
			||||||
 | 
								d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							xbs = d.r.readx(l)
 | 
				
			||||||
 | 
						} else if d.vd == bincVdByteArray {
 | 
				
			||||||
 | 
							xbs = d.DecodeBytes(nil, true)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("Invalid d.vd for extensions (Expecting extensions or byte array). Got: 0x%x", d.vd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) DecodeNaked() {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						n := d.d.n
 | 
				
			||||||
 | 
						var decodeFurther bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch d.vd {
 | 
				
			||||||
 | 
						case bincVdSpecial:
 | 
				
			||||||
 | 
							switch d.vs {
 | 
				
			||||||
 | 
							case bincSpNil:
 | 
				
			||||||
 | 
								n.v = valueTypeNil
 | 
				
			||||||
 | 
							case bincSpFalse:
 | 
				
			||||||
 | 
								n.v = valueTypeBool
 | 
				
			||||||
 | 
								n.b = false
 | 
				
			||||||
 | 
							case bincSpTrue:
 | 
				
			||||||
 | 
								n.v = valueTypeBool
 | 
				
			||||||
 | 
								n.b = true
 | 
				
			||||||
 | 
							case bincSpNan:
 | 
				
			||||||
 | 
								n.v = valueTypeFloat
 | 
				
			||||||
 | 
								n.f = math.NaN()
 | 
				
			||||||
 | 
							case bincSpPosInf:
 | 
				
			||||||
 | 
								n.v = valueTypeFloat
 | 
				
			||||||
 | 
								n.f = math.Inf(1)
 | 
				
			||||||
 | 
							case bincSpNegInf:
 | 
				
			||||||
 | 
								n.v = valueTypeFloat
 | 
				
			||||||
 | 
								n.f = math.Inf(-1)
 | 
				
			||||||
 | 
							case bincSpZeroFloat:
 | 
				
			||||||
 | 
								n.v = valueTypeFloat
 | 
				
			||||||
 | 
								n.f = float64(0)
 | 
				
			||||||
 | 
							case bincSpZero:
 | 
				
			||||||
 | 
								n.v = valueTypeUint
 | 
				
			||||||
 | 
								n.u = uint64(0) // int8(0)
 | 
				
			||||||
 | 
							case bincSpNegOne:
 | 
				
			||||||
 | 
								n.v = valueTypeInt
 | 
				
			||||||
 | 
								n.i = int64(-1) // int8(-1)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								d.d.errorf("decodeNaked: Unrecognized special value 0x%x", d.vs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case bincVdSmallInt:
 | 
				
			||||||
 | 
							n.v = valueTypeUint
 | 
				
			||||||
 | 
							n.u = uint64(int8(d.vs)) + 1 // int8(d.vs) + 1
 | 
				
			||||||
 | 
						case bincVdPosInt:
 | 
				
			||||||
 | 
							n.v = valueTypeUint
 | 
				
			||||||
 | 
							n.u = d.decUint()
 | 
				
			||||||
 | 
						case bincVdNegInt:
 | 
				
			||||||
 | 
							n.v = valueTypeInt
 | 
				
			||||||
 | 
							n.i = -(int64(d.decUint()))
 | 
				
			||||||
 | 
						case bincVdFloat:
 | 
				
			||||||
 | 
							n.v = valueTypeFloat
 | 
				
			||||||
 | 
							n.f = d.decFloat()
 | 
				
			||||||
 | 
						case bincVdSymbol:
 | 
				
			||||||
 | 
							n.v = valueTypeSymbol
 | 
				
			||||||
 | 
							n.s = d.DecodeString()
 | 
				
			||||||
 | 
						case bincVdString:
 | 
				
			||||||
 | 
							n.v = valueTypeString
 | 
				
			||||||
 | 
							n.s = d.DecodeString()
 | 
				
			||||||
 | 
						case bincVdByteArray:
 | 
				
			||||||
 | 
							n.v = valueTypeBytes
 | 
				
			||||||
 | 
							n.l = d.DecodeBytes(nil, false)
 | 
				
			||||||
 | 
						case bincVdTimestamp:
 | 
				
			||||||
 | 
							n.v = valueTypeTimestamp
 | 
				
			||||||
 | 
							tt, err := decodeTime(d.r.readx(int(d.vs)))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							n.t = tt
 | 
				
			||||||
 | 
						case bincVdCustomExt:
 | 
				
			||||||
 | 
							n.v = valueTypeExt
 | 
				
			||||||
 | 
							l := d.decLen()
 | 
				
			||||||
 | 
							n.u = uint64(d.r.readn1())
 | 
				
			||||||
 | 
							n.l = d.r.readx(l)
 | 
				
			||||||
 | 
						case bincVdArray:
 | 
				
			||||||
 | 
							n.v = valueTypeArray
 | 
				
			||||||
 | 
							decodeFurther = true
 | 
				
			||||||
 | 
						case bincVdMap:
 | 
				
			||||||
 | 
							n.v = valueTypeMap
 | 
				
			||||||
 | 
							decodeFurther = true
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							d.d.errorf("decodeNaked: Unrecognized d.vd: 0x%x", d.vd)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !decodeFurther {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if n.v == valueTypeUint && d.h.SignedInteger {
 | 
				
			||||||
 | 
							n.v = valueTypeInt
 | 
				
			||||||
 | 
							n.i = int64(n.u)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//BincHandle is a Handle for the Binc Schema-Free Encoding Format
 | 
				
			||||||
 | 
					//defined at https://github.com/ugorji/binc .
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//BincHandle currently supports all Binc features with the following EXCEPTIONS:
 | 
				
			||||||
 | 
					//  - only integers up to 64 bits of precision are supported.
 | 
				
			||||||
 | 
					//    big integers are unsupported.
 | 
				
			||||||
 | 
					//  - Only IEEE 754 binary32 and binary64 floats are supported (ie Go float32 and float64 types).
 | 
				
			||||||
 | 
					//    extended precision and decimal IEEE 754 floats are unsupported.
 | 
				
			||||||
 | 
					//  - Only UTF-8 strings supported.
 | 
				
			||||||
 | 
					//    Unicode_Other Binc types (UTF16, UTF32) are currently unsupported.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//Note that these EXCEPTIONS are temporary and full support is possible and may happen soon.
 | 
				
			||||||
 | 
					type BincHandle struct {
 | 
				
			||||||
 | 
						BasicHandle
 | 
				
			||||||
 | 
						binaryEncodingType
 | 
				
			||||||
 | 
						noElemSeparators
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *BincHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
 | 
				
			||||||
 | 
						return h.SetExt(rt, tag, &setExtWrapper{b: ext})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *BincHandle) newEncDriver(e *Encoder) encDriver {
 | 
				
			||||||
 | 
						return &bincEncDriver{e: e, w: e.w}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *BincHandle) newDecDriver(d *Decoder) decDriver {
 | 
				
			||||||
 | 
						return &bincDecDriver{d: d, h: h, r: d.r, br: d.bytes}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (_ *BincHandle) IsBuiltinType(rt uintptr) bool {
 | 
				
			||||||
 | 
						return rt == timeTypId
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *bincEncDriver) reset() {
 | 
				
			||||||
 | 
						e.w = e.e.w
 | 
				
			||||||
 | 
						e.s = 0
 | 
				
			||||||
 | 
						e.m = nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *bincDecDriver) reset() {
 | 
				
			||||||
 | 
						d.r, d.br = d.d.r, d.d.bytes
 | 
				
			||||||
 | 
						d.s = nil
 | 
				
			||||||
 | 
						d.bd, d.bdRead, d.vd, d.vs = 0, false, 0, 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ decDriver = (*bincDecDriver)(nil)
 | 
				
			||||||
 | 
					var _ encDriver = (*bincEncDriver)(nil)
 | 
				
			||||||
							
								
								
									
										631
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/cbor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										631
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/cbor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,631 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						cborMajorUint byte = iota
 | 
				
			||||||
 | 
						cborMajorNegInt
 | 
				
			||||||
 | 
						cborMajorBytes
 | 
				
			||||||
 | 
						cborMajorText
 | 
				
			||||||
 | 
						cborMajorArray
 | 
				
			||||||
 | 
						cborMajorMap
 | 
				
			||||||
 | 
						cborMajorTag
 | 
				
			||||||
 | 
						cborMajorOther
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						cborBdFalse byte = 0xf4 + iota
 | 
				
			||||||
 | 
						cborBdTrue
 | 
				
			||||||
 | 
						cborBdNil
 | 
				
			||||||
 | 
						cborBdUndefined
 | 
				
			||||||
 | 
						cborBdExt
 | 
				
			||||||
 | 
						cborBdFloat16
 | 
				
			||||||
 | 
						cborBdFloat32
 | 
				
			||||||
 | 
						cborBdFloat64
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						cborBdIndefiniteBytes  byte = 0x5f
 | 
				
			||||||
 | 
						cborBdIndefiniteString      = 0x7f
 | 
				
			||||||
 | 
						cborBdIndefiniteArray       = 0x9f
 | 
				
			||||||
 | 
						cborBdIndefiniteMap         = 0xbf
 | 
				
			||||||
 | 
						cborBdBreak                 = 0xff
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						CborStreamBytes  byte = 0x5f
 | 
				
			||||||
 | 
						CborStreamString      = 0x7f
 | 
				
			||||||
 | 
						CborStreamArray       = 0x9f
 | 
				
			||||||
 | 
						CborStreamMap         = 0xbf
 | 
				
			||||||
 | 
						CborStreamBreak       = 0xff
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						cborBaseUint   byte = 0x00
 | 
				
			||||||
 | 
						cborBaseNegInt      = 0x20
 | 
				
			||||||
 | 
						cborBaseBytes       = 0x40
 | 
				
			||||||
 | 
						cborBaseString      = 0x60
 | 
				
			||||||
 | 
						cborBaseArray       = 0x80
 | 
				
			||||||
 | 
						cborBaseMap         = 0xa0
 | 
				
			||||||
 | 
						cborBaseTag         = 0xc0
 | 
				
			||||||
 | 
						cborBaseSimple      = 0xe0
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// -------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cborEncDriver struct {
 | 
				
			||||||
 | 
						noBuiltInTypes
 | 
				
			||||||
 | 
						encDriverNoopContainerWriter
 | 
				
			||||||
 | 
						// encNoSeparator
 | 
				
			||||||
 | 
						e *Encoder
 | 
				
			||||||
 | 
						w encWriter
 | 
				
			||||||
 | 
						h *CborHandle
 | 
				
			||||||
 | 
						x [8]byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeNil() {
 | 
				
			||||||
 | 
						e.w.writen1(cborBdNil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeBool(b bool) {
 | 
				
			||||||
 | 
						if b {
 | 
				
			||||||
 | 
							e.w.writen1(cborBdTrue)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.w.writen1(cborBdFalse)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeFloat32(f float32) {
 | 
				
			||||||
 | 
						e.w.writen1(cborBdFloat32)
 | 
				
			||||||
 | 
						bigenHelper{e.x[:4], e.w}.writeUint32(math.Float32bits(f))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeFloat64(f float64) {
 | 
				
			||||||
 | 
						e.w.writen1(cborBdFloat64)
 | 
				
			||||||
 | 
						bigenHelper{e.x[:8], e.w}.writeUint64(math.Float64bits(f))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) encUint(v uint64, bd byte) {
 | 
				
			||||||
 | 
						if v <= 0x17 {
 | 
				
			||||||
 | 
							e.w.writen1(byte(v) + bd)
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint8 {
 | 
				
			||||||
 | 
							e.w.writen2(bd+0x18, uint8(v))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint16 {
 | 
				
			||||||
 | 
							e.w.writen1(bd + 0x19)
 | 
				
			||||||
 | 
							bigenHelper{e.x[:2], e.w}.writeUint16(uint16(v))
 | 
				
			||||||
 | 
						} else if v <= math.MaxUint32 {
 | 
				
			||||||
 | 
							e.w.writen1(bd + 0x1a)
 | 
				
			||||||
 | 
							bigenHelper{e.x[:4], e.w}.writeUint32(uint32(v))
 | 
				
			||||||
 | 
						} else { // if v <= math.MaxUint64 {
 | 
				
			||||||
 | 
							e.w.writen1(bd + 0x1b)
 | 
				
			||||||
 | 
							bigenHelper{e.x[:8], e.w}.writeUint64(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeInt(v int64) {
 | 
				
			||||||
 | 
						if v < 0 {
 | 
				
			||||||
 | 
							e.encUint(uint64(-1-v), cborBaseNegInt)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encUint(uint64(v), cborBaseUint)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeUint(v uint64) {
 | 
				
			||||||
 | 
						e.encUint(v, cborBaseUint)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) encLen(bd byte, length int) {
 | 
				
			||||||
 | 
						e.encUint(uint64(length), bd)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, en *Encoder) {
 | 
				
			||||||
 | 
						e.encUint(uint64(xtag), cborBaseTag)
 | 
				
			||||||
 | 
						if v := ext.ConvertExt(rv); v == nil {
 | 
				
			||||||
 | 
							e.EncodeNil()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							en.encode(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeRawExt(re *RawExt, en *Encoder) {
 | 
				
			||||||
 | 
						e.encUint(uint64(re.Tag), cborBaseTag)
 | 
				
			||||||
 | 
						if false && re.Data != nil {
 | 
				
			||||||
 | 
							en.encode(re.Data)
 | 
				
			||||||
 | 
						} else if re.Value != nil {
 | 
				
			||||||
 | 
							en.encode(re.Value)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.EncodeNil()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) WriteArrayStart(length int) {
 | 
				
			||||||
 | 
						if e.h.IndefiniteLength {
 | 
				
			||||||
 | 
							e.w.writen1(cborBdIndefiniteArray)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encLen(cborBaseArray, length)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) WriteMapStart(length int) {
 | 
				
			||||||
 | 
						if e.h.IndefiniteLength {
 | 
				
			||||||
 | 
							e.w.writen1(cborBdIndefiniteMap)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encLen(cborBaseMap, length)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) WriteMapEnd() {
 | 
				
			||||||
 | 
						if e.h.IndefiniteLength {
 | 
				
			||||||
 | 
							e.w.writen1(cborBdBreak)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) WriteArrayEnd() {
 | 
				
			||||||
 | 
						if e.h.IndefiniteLength {
 | 
				
			||||||
 | 
							e.w.writen1(cborBdBreak)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeString(c charEncoding, v string) {
 | 
				
			||||||
 | 
						e.encLen(cborBaseString, len(v))
 | 
				
			||||||
 | 
						e.w.writestr(v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeSymbol(v string) {
 | 
				
			||||||
 | 
						e.EncodeString(c_UTF8, v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
 | 
				
			||||||
 | 
						if c == c_RAW {
 | 
				
			||||||
 | 
							e.encLen(cborBaseBytes, len(v))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							e.encLen(cborBaseString, len(v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e.w.writeb(v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type cborDecDriver struct {
 | 
				
			||||||
 | 
						d      *Decoder
 | 
				
			||||||
 | 
						h      *CborHandle
 | 
				
			||||||
 | 
						r      decReader
 | 
				
			||||||
 | 
						b      [scratchByteArrayLen]byte
 | 
				
			||||||
 | 
						br     bool // bytes reader
 | 
				
			||||||
 | 
						bdRead bool
 | 
				
			||||||
 | 
						bd     byte
 | 
				
			||||||
 | 
						noBuiltInTypes
 | 
				
			||||||
 | 
						// decNoSeparator
 | 
				
			||||||
 | 
						decDriverNoopContainerReader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) readNextBd() {
 | 
				
			||||||
 | 
						d.bd = d.r.readn1()
 | 
				
			||||||
 | 
						d.bdRead = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) uncacheRead() {
 | 
				
			||||||
 | 
						if d.bdRead {
 | 
				
			||||||
 | 
							d.r.unreadn1()
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) ContainerType() (vt valueType) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == cborBdNil {
 | 
				
			||||||
 | 
							return valueTypeNil
 | 
				
			||||||
 | 
						} else if d.bd == cborBdIndefiniteBytes || (d.bd >= cborBaseBytes && d.bd < cborBaseString) {
 | 
				
			||||||
 | 
							return valueTypeBytes
 | 
				
			||||||
 | 
						} else if d.bd == cborBdIndefiniteString || (d.bd >= cborBaseString && d.bd < cborBaseArray) {
 | 
				
			||||||
 | 
							return valueTypeString
 | 
				
			||||||
 | 
						} else if d.bd == cborBdIndefiniteArray || (d.bd >= cborBaseArray && d.bd < cborBaseMap) {
 | 
				
			||||||
 | 
							return valueTypeArray
 | 
				
			||||||
 | 
						} else if d.bd == cborBdIndefiniteMap || (d.bd >= cborBaseMap && d.bd < cborBaseTag) {
 | 
				
			||||||
 | 
							return valueTypeMap
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return valueTypeUnset
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) TryDecodeAsNil() bool {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// treat Nil and Undefined as nil values
 | 
				
			||||||
 | 
						if d.bd == cborBdNil || d.bd == cborBdUndefined {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) CheckBreak() bool {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == cborBdBreak {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) decUint() (ui uint64) {
 | 
				
			||||||
 | 
						v := d.bd & 0x1f
 | 
				
			||||||
 | 
						if v <= 0x17 {
 | 
				
			||||||
 | 
							ui = uint64(v)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if v == 0x18 {
 | 
				
			||||||
 | 
								ui = uint64(d.r.readn1())
 | 
				
			||||||
 | 
							} else if v == 0x19 {
 | 
				
			||||||
 | 
								ui = uint64(bigen.Uint16(d.r.readx(2)))
 | 
				
			||||||
 | 
							} else if v == 0x1a {
 | 
				
			||||||
 | 
								ui = uint64(bigen.Uint32(d.r.readx(4)))
 | 
				
			||||||
 | 
							} else if v == 0x1b {
 | 
				
			||||||
 | 
								ui = uint64(bigen.Uint64(d.r.readx(8)))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								d.d.errorf("decUint: Invalid descriptor: %v", d.bd)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) decCheckInteger() (neg bool) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						major := d.bd >> 5
 | 
				
			||||||
 | 
						if major == cborMajorUint {
 | 
				
			||||||
 | 
						} else if major == cborMajorNegInt {
 | 
				
			||||||
 | 
							neg = true
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("invalid major: %v (bd: %v)", major, d.bd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeInt(bitsize uint8) (i int64) {
 | 
				
			||||||
 | 
						neg := d.decCheckInteger()
 | 
				
			||||||
 | 
						ui := d.decUint()
 | 
				
			||||||
 | 
						// check if this number can be converted to an int without overflow
 | 
				
			||||||
 | 
						var overflow bool
 | 
				
			||||||
 | 
						if neg {
 | 
				
			||||||
 | 
							if i, overflow = chkOvf.SignedInt(ui + 1); overflow {
 | 
				
			||||||
 | 
								d.d.errorf("cbor: overflow converting %v to signed integer", ui+1)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							i = -i
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if i, overflow = chkOvf.SignedInt(ui); overflow {
 | 
				
			||||||
 | 
								d.d.errorf("cbor: overflow converting %v to signed integer", ui)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if chkOvf.Int(i, bitsize) {
 | 
				
			||||||
 | 
							d.d.errorf("cbor: overflow integer: %v", i)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
 | 
				
			||||||
 | 
						if d.decCheckInteger() {
 | 
				
			||||||
 | 
							d.d.errorf("Assigning negative signed value to unsigned type")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ui = d.decUint()
 | 
				
			||||||
 | 
						if chkOvf.Uint(ui, bitsize) {
 | 
				
			||||||
 | 
							d.d.errorf("cbor: overflow integer: %v", ui)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if bd := d.bd; bd == cborBdFloat16 {
 | 
				
			||||||
 | 
							f = float64(math.Float32frombits(halfFloatToFloatBits(bigen.Uint16(d.r.readx(2)))))
 | 
				
			||||||
 | 
						} else if bd == cborBdFloat32 {
 | 
				
			||||||
 | 
							f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
 | 
				
			||||||
 | 
						} else if bd == cborBdFloat64 {
 | 
				
			||||||
 | 
							f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
 | 
				
			||||||
 | 
						} else if bd >= cborBaseUint && bd < cborBaseBytes {
 | 
				
			||||||
 | 
							f = float64(d.DecodeInt(64))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("Float only valid from float16/32/64: Invalid descriptor: %v", bd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if chkOverflow32 && chkOvf.Float32(f) {
 | 
				
			||||||
 | 
							d.d.errorf("cbor: float32 overflow: %v", f)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// bool can be decoded from bool only (single byte).
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeBool() (b bool) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if bd := d.bd; bd == cborBdTrue {
 | 
				
			||||||
 | 
							b = true
 | 
				
			||||||
 | 
						} else if bd == cborBdFalse {
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) ReadMapStart() (length int) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						if d.bd == cborBdIndefiniteMap {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.decLen()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) ReadArrayStart() (length int) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						if d.bd == cborBdIndefiniteArray {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.decLen()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) decLen() int {
 | 
				
			||||||
 | 
						return int(d.decUint())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) decAppendIndefiniteBytes(bs []byte) []byte {
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							if d.CheckBreak() {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if major := d.bd >> 5; major != cborMajorBytes && major != cborMajorText {
 | 
				
			||||||
 | 
								d.d.errorf("cbor: expect bytes or string major type in indefinite string/bytes; got: %v, byte: %v", major, d.bd)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							n := d.decLen()
 | 
				
			||||||
 | 
							oldLen := len(bs)
 | 
				
			||||||
 | 
							newLen := oldLen + n
 | 
				
			||||||
 | 
							if newLen > cap(bs) {
 | 
				
			||||||
 | 
								bs2 := make([]byte, newLen, 2*cap(bs)+n)
 | 
				
			||||||
 | 
								copy(bs2, bs)
 | 
				
			||||||
 | 
								bs = bs2
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								bs = bs[:newLen]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							d.r.readb(bs[oldLen:newLen])
 | 
				
			||||||
 | 
							// bs = append(bs, d.r.readn()...)
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return bs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == cborBdNil || d.bd == cborBdUndefined {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.bd == cborBdIndefiniteBytes || d.bd == cborBdIndefiniteString {
 | 
				
			||||||
 | 
							if bs == nil {
 | 
				
			||||||
 | 
								return d.decAppendIndefiniteBytes(nil)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return d.decAppendIndefiniteBytes(bs[:0])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						clen := d.decLen()
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						if zerocopy {
 | 
				
			||||||
 | 
							if d.br {
 | 
				
			||||||
 | 
								return d.r.readx(clen)
 | 
				
			||||||
 | 
							} else if len(bs) == 0 {
 | 
				
			||||||
 | 
								bs = d.b[:]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return decByteSlice(d.r, clen, d.d.h.MaxInitLen, bs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeString() (s string) {
 | 
				
			||||||
 | 
						return string(d.DecodeBytes(d.b[:], true))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeStringAsBytes() (s []byte) {
 | 
				
			||||||
 | 
						return d.DecodeBytes(d.b[:], true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						u := d.decUint()
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						realxtag = u
 | 
				
			||||||
 | 
						if ext == nil {
 | 
				
			||||||
 | 
							re := rv.(*RawExt)
 | 
				
			||||||
 | 
							re.Tag = realxtag
 | 
				
			||||||
 | 
							d.d.decode(&re.Value)
 | 
				
			||||||
 | 
						} else if xtag != realxtag {
 | 
				
			||||||
 | 
							d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", realxtag, xtag)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							var v interface{}
 | 
				
			||||||
 | 
							d.d.decode(&v)
 | 
				
			||||||
 | 
							ext.UpdateExt(rv, v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.bdRead = false
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) DecodeNaked() {
 | 
				
			||||||
 | 
						if !d.bdRead {
 | 
				
			||||||
 | 
							d.readNextBd()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						n := d.d.n
 | 
				
			||||||
 | 
						var decodeFurther bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch d.bd {
 | 
				
			||||||
 | 
						case cborBdNil:
 | 
				
			||||||
 | 
							n.v = valueTypeNil
 | 
				
			||||||
 | 
						case cborBdFalse:
 | 
				
			||||||
 | 
							n.v = valueTypeBool
 | 
				
			||||||
 | 
							n.b = false
 | 
				
			||||||
 | 
						case cborBdTrue:
 | 
				
			||||||
 | 
							n.v = valueTypeBool
 | 
				
			||||||
 | 
							n.b = true
 | 
				
			||||||
 | 
						case cborBdFloat16, cborBdFloat32:
 | 
				
			||||||
 | 
							n.v = valueTypeFloat
 | 
				
			||||||
 | 
							n.f = d.DecodeFloat(true)
 | 
				
			||||||
 | 
						case cborBdFloat64:
 | 
				
			||||||
 | 
							n.v = valueTypeFloat
 | 
				
			||||||
 | 
							n.f = d.DecodeFloat(false)
 | 
				
			||||||
 | 
						case cborBdIndefiniteBytes:
 | 
				
			||||||
 | 
							n.v = valueTypeBytes
 | 
				
			||||||
 | 
							n.l = d.DecodeBytes(nil, false)
 | 
				
			||||||
 | 
						case cborBdIndefiniteString:
 | 
				
			||||||
 | 
							n.v = valueTypeString
 | 
				
			||||||
 | 
							n.s = d.DecodeString()
 | 
				
			||||||
 | 
						case cborBdIndefiniteArray:
 | 
				
			||||||
 | 
							n.v = valueTypeArray
 | 
				
			||||||
 | 
							decodeFurther = true
 | 
				
			||||||
 | 
						case cborBdIndefiniteMap:
 | 
				
			||||||
 | 
							n.v = valueTypeMap
 | 
				
			||||||
 | 
							decodeFurther = true
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case d.bd >= cborBaseUint && d.bd < cborBaseNegInt:
 | 
				
			||||||
 | 
								if d.h.SignedInteger {
 | 
				
			||||||
 | 
									n.v = valueTypeInt
 | 
				
			||||||
 | 
									n.i = d.DecodeInt(64)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									n.v = valueTypeUint
 | 
				
			||||||
 | 
									n.u = d.DecodeUint(64)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case d.bd >= cborBaseNegInt && d.bd < cborBaseBytes:
 | 
				
			||||||
 | 
								n.v = valueTypeInt
 | 
				
			||||||
 | 
								n.i = d.DecodeInt(64)
 | 
				
			||||||
 | 
							case d.bd >= cborBaseBytes && d.bd < cborBaseString:
 | 
				
			||||||
 | 
								n.v = valueTypeBytes
 | 
				
			||||||
 | 
								n.l = d.DecodeBytes(nil, false)
 | 
				
			||||||
 | 
							case d.bd >= cborBaseString && d.bd < cborBaseArray:
 | 
				
			||||||
 | 
								n.v = valueTypeString
 | 
				
			||||||
 | 
								n.s = d.DecodeString()
 | 
				
			||||||
 | 
							case d.bd >= cborBaseArray && d.bd < cborBaseMap:
 | 
				
			||||||
 | 
								n.v = valueTypeArray
 | 
				
			||||||
 | 
								decodeFurther = true
 | 
				
			||||||
 | 
							case d.bd >= cborBaseMap && d.bd < cborBaseTag:
 | 
				
			||||||
 | 
								n.v = valueTypeMap
 | 
				
			||||||
 | 
								decodeFurther = true
 | 
				
			||||||
 | 
							case d.bd >= cborBaseTag && d.bd < cborBaseSimple:
 | 
				
			||||||
 | 
								n.v = valueTypeExt
 | 
				
			||||||
 | 
								n.u = d.decUint()
 | 
				
			||||||
 | 
								n.l = nil
 | 
				
			||||||
 | 
								// d.bdRead = false
 | 
				
			||||||
 | 
								// d.d.decode(&re.Value) // handled by decode itself.
 | 
				
			||||||
 | 
								// decodeFurther = true
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								d.d.errorf("decodeNaked: Unrecognized d.bd: 0x%x", d.bd)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !decodeFurther {
 | 
				
			||||||
 | 
							d.bdRead = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// -------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CborHandle is a Handle for the CBOR encoding format,
 | 
				
			||||||
 | 
					// defined at http://tools.ietf.org/html/rfc7049 and documented further at http://cbor.io .
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// CBOR is comprehensively supported, including support for:
 | 
				
			||||||
 | 
					//   - indefinite-length arrays/maps/bytes/strings
 | 
				
			||||||
 | 
					//   - (extension) tags in range 0..0xffff (0 .. 65535)
 | 
				
			||||||
 | 
					//   - half, single and double-precision floats
 | 
				
			||||||
 | 
					//   - all numbers (1, 2, 4 and 8-byte signed and unsigned integers)
 | 
				
			||||||
 | 
					//   - nil, true, false, ...
 | 
				
			||||||
 | 
					//   - arrays and maps, bytes and text strings
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// None of the optional extensions (with tags) defined in the spec are supported out-of-the-box.
 | 
				
			||||||
 | 
					// Users can implement them as needed (using SetExt), including spec-documented ones:
 | 
				
			||||||
 | 
					//   - timestamp, BigNum, BigFloat, Decimals, Encoded Text (e.g. URL, regexp, base64, MIME Message), etc.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// To encode with indefinite lengths (streaming), users will use
 | 
				
			||||||
 | 
					// (Must)Encode methods of *Encoder, along with writing CborStreamXXX constants.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// For example, to encode "one-byte" as an indefinite length string:
 | 
				
			||||||
 | 
					//     var buf bytes.Buffer
 | 
				
			||||||
 | 
					//     e := NewEncoder(&buf, new(CborHandle))
 | 
				
			||||||
 | 
					//     buf.WriteByte(CborStreamString)
 | 
				
			||||||
 | 
					//     e.MustEncode("one-")
 | 
				
			||||||
 | 
					//     e.MustEncode("byte")
 | 
				
			||||||
 | 
					//     buf.WriteByte(CborStreamBreak)
 | 
				
			||||||
 | 
					//     encodedBytes := buf.Bytes()
 | 
				
			||||||
 | 
					//     var vv interface{}
 | 
				
			||||||
 | 
					//     NewDecoderBytes(buf.Bytes(), new(CborHandle)).MustDecode(&vv)
 | 
				
			||||||
 | 
					//     // Now, vv contains the same string "one-byte"
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					type CborHandle struct {
 | 
				
			||||||
 | 
						binaryEncodingType
 | 
				
			||||||
 | 
						noElemSeparators
 | 
				
			||||||
 | 
						BasicHandle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// IndefiniteLength=true, means that we encode using indefinitelength
 | 
				
			||||||
 | 
						IndefiniteLength bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *CborHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
 | 
				
			||||||
 | 
						return h.SetExt(rt, tag, &setExtWrapper{i: ext})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *CborHandle) newEncDriver(e *Encoder) encDriver {
 | 
				
			||||||
 | 
						return &cborEncDriver{e: e, w: e.w, h: h}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *CborHandle) newDecDriver(d *Decoder) decDriver {
 | 
				
			||||||
 | 
						return &cborDecDriver{d: d, h: h, r: d.r, br: d.bytes}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *cborEncDriver) reset() {
 | 
				
			||||||
 | 
						e.w = e.e.w
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *cborDecDriver) reset() {
 | 
				
			||||||
 | 
						d.r, d.br = d.d.r, d.d.bytes
 | 
				
			||||||
 | 
						d.bd, d.bdRead = 0, false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ decDriver = (*cborDecDriver)(nil)
 | 
				
			||||||
 | 
					var _ encDriver = (*cborEncDriver)(nil)
 | 
				
			||||||
							
								
								
									
										2520
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2520
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1414
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/encode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1414
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/encode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										33034
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/fast-path.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33034
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/fast-path.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										35
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/fast-path.not.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/fast-path.not.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					// +build notfastpath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fastpathEnabled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The generated fast-path code is very large, and adds a few seconds to the build time.
 | 
				
			||||||
 | 
					// This causes test execution, execution of small tools which use codec, etc
 | 
				
			||||||
 | 
					// to take a long time.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// To mitigate, we now support the notfastpath tag.
 | 
				
			||||||
 | 
					// This tag disables fastpath during build, allowing for faster build, test execution,
 | 
				
			||||||
 | 
					// short-program runs, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool        { return false }
 | 
				
			||||||
 | 
					func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool        { return false }
 | 
				
			||||||
 | 
					func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool   { return false }
 | 
				
			||||||
 | 
					func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool     { return false }
 | 
				
			||||||
 | 
					func fastpathDecodeSetZeroTypeSwitch(iv interface{}, d *Decoder) bool { return false }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fastpathT struct{}
 | 
				
			||||||
 | 
					type fastpathE struct {
 | 
				
			||||||
 | 
						rtid  uintptr
 | 
				
			||||||
 | 
						rt    reflect.Type
 | 
				
			||||||
 | 
						encfn func(*Encoder, *codecFnInfo, reflect.Value)
 | 
				
			||||||
 | 
						decfn func(*Decoder, *codecFnInfo, reflect.Value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					type fastpathA [0]fastpathE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x fastpathA) index(rtid uintptr) int { return -1 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var fastpathAV fastpathA
 | 
				
			||||||
 | 
					var fastpathTV fastpathT
 | 
				
			||||||
							
								
								
									
										250
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/gen-helper.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/gen-helper.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,250 @@
 | 
				
			|||||||
 | 
					/* // +build ignore */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ************************************************************
 | 
				
			||||||
 | 
					// DO NOT EDIT.
 | 
				
			||||||
 | 
					// THIS FILE IS AUTO-GENERATED from gen-helper.go.tmpl
 | 
				
			||||||
 | 
					// ************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenVersion is the current version of codecgen.
 | 
				
			||||||
 | 
					const GenVersion = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This file is used to generate helper code for codecgen.
 | 
				
			||||||
 | 
					// The values here i.e. genHelper(En|De)coder are not to be used directly by
 | 
				
			||||||
 | 
					// library users. They WILL change continuously and without notice.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// To help enforce this, we create an unexported type with exported members.
 | 
				
			||||||
 | 
					// The only way to get the type is via the one exported type that we control (somewhat).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// When static codecs are created for types, they will use this value
 | 
				
			||||||
 | 
					// to perform encoding or decoding of primitives or known slice or map types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenHelperEncoder is exported so that it can be used externally by codecgen.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 | 
				
			||||||
 | 
					func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
 | 
				
			||||||
 | 
						return genHelperEncoder{e: e}, e.e
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenHelperDecoder is exported so that it can be used externally by codecgen.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 | 
				
			||||||
 | 
					func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
 | 
				
			||||||
 | 
						return genHelperDecoder{d: d}, d.d
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 | 
				
			||||||
 | 
					func BasicHandleDoNotUse(h Handle) *BasicHandle {
 | 
				
			||||||
 | 
						return h.getBasicHandle()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					type genHelperEncoder struct {
 | 
				
			||||||
 | 
						e *Encoder
 | 
				
			||||||
 | 
						F fastpathT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					type genHelperDecoder struct {
 | 
				
			||||||
 | 
						d *Decoder
 | 
				
			||||||
 | 
						F fastpathT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
 | 
				
			||||||
 | 
						return f.e.h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncBinary() bool {
 | 
				
			||||||
 | 
						return f.e.cf.be // f.e.hh.isBinaryEncoding()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncFallback(iv interface{}) {
 | 
				
			||||||
 | 
						// println(">>>>>>>>> EncFallback")
 | 
				
			||||||
 | 
						// f.e.encodeI(iv, false, false)
 | 
				
			||||||
 | 
						f.e.encodeValue(reflect.ValueOf(iv), nil, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
 | 
				
			||||||
 | 
						bs, fnerr := iv.MarshalText()
 | 
				
			||||||
 | 
						f.e.marshal(bs, fnerr, false, c_UTF8)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
 | 
				
			||||||
 | 
						bs, fnerr := iv.MarshalJSON()
 | 
				
			||||||
 | 
						f.e.marshal(bs, fnerr, true, c_UTF8)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
 | 
				
			||||||
 | 
						bs, fnerr := iv.MarshalBinary()
 | 
				
			||||||
 | 
						f.e.marshal(bs, fnerr, false, c_RAW)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncRaw(iv Raw) {
 | 
				
			||||||
 | 
						f.e.rawBytes(iv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
 | 
				
			||||||
 | 
						if _, ok := f.e.hh.(*BincHandle); ok {
 | 
				
			||||||
 | 
							return timeTypId
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) IsJSONHandle() bool {
 | 
				
			||||||
 | 
						return f.e.cf.js
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) HasExtensions() bool {
 | 
				
			||||||
 | 
						return len(f.e.h.extHandle) != 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
 | 
				
			||||||
 | 
						rt := reflect.TypeOf(v)
 | 
				
			||||||
 | 
						if rt.Kind() == reflect.Ptr {
 | 
				
			||||||
 | 
							rt = rt.Elem()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rtid := rt2id(rt)
 | 
				
			||||||
 | 
						if xfFn := f.e.h.getExt(rtid); xfFn != nil {
 | 
				
			||||||
 | 
							f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ---------------- DECODER FOLLOWS -----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
 | 
				
			||||||
 | 
						return f.d.h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecBinary() bool {
 | 
				
			||||||
 | 
						return f.d.be // f.d.hh.isBinaryEncoding()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecSwallow() {
 | 
				
			||||||
 | 
						f.d.swallow()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecScratchBuffer() []byte {
 | 
				
			||||||
 | 
						return f.d.b[:]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
 | 
				
			||||||
 | 
						// println(">>>>>>>>> DecFallback")
 | 
				
			||||||
 | 
						rv := reflect.ValueOf(iv)
 | 
				
			||||||
 | 
						if chkPtr {
 | 
				
			||||||
 | 
							rv = f.d.ensureDecodeable(rv)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						f.d.decodeValue(rv, nil, false, false)
 | 
				
			||||||
 | 
						// f.d.decodeValueFallback(rv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecSliceHelperStart() (decSliceHelper, int) {
 | 
				
			||||||
 | 
						return f.d.decSliceHelperStart()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
 | 
				
			||||||
 | 
						f.d.structFieldNotFound(index, name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
 | 
				
			||||||
 | 
						f.d.arrayCannotExpand(sliceLen, streamLen)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
 | 
				
			||||||
 | 
						fnerr := tm.UnmarshalText(f.d.d.DecodeStringAsBytes())
 | 
				
			||||||
 | 
						if fnerr != nil {
 | 
				
			||||||
 | 
							panic(fnerr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
 | 
				
			||||||
 | 
						// bs := f.dd.DecodeStringAsBytes()
 | 
				
			||||||
 | 
						// grab the bytes to be read, as UnmarshalJSON needs the full JSON so as to unmarshal it itself.
 | 
				
			||||||
 | 
						fnerr := tm.UnmarshalJSON(f.d.nextValueBytes())
 | 
				
			||||||
 | 
						if fnerr != nil {
 | 
				
			||||||
 | 
							panic(fnerr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
 | 
				
			||||||
 | 
						fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, true))
 | 
				
			||||||
 | 
						if fnerr != nil {
 | 
				
			||||||
 | 
							panic(fnerr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecRaw() []byte {
 | 
				
			||||||
 | 
						return f.d.rawBytes()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
 | 
				
			||||||
 | 
						if _, ok := f.d.hh.(*BincHandle); ok {
 | 
				
			||||||
 | 
							return timeTypId
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) IsJSONHandle() bool {
 | 
				
			||||||
 | 
						return f.d.js
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) HasExtensions() bool {
 | 
				
			||||||
 | 
						return len(f.d.h.extHandle) != 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
 | 
				
			||||||
 | 
						rt := reflect.TypeOf(v).Elem()
 | 
				
			||||||
 | 
						rtid := rt2id(rt)
 | 
				
			||||||
 | 
						if xfFn := f.d.h.getExt(rtid); xfFn != nil {
 | 
				
			||||||
 | 
							f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int) {
 | 
				
			||||||
 | 
						return decInferLen(clen, maxlen, unit)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 | 
				
			||||||
 | 
					func (f genHelperDecoder) StringView(v []byte) string {
 | 
				
			||||||
 | 
						return stringView(v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/gen.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/gen.generated.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DO NOT EDIT. THIS FILE IS AUTO-GENERATED FROM gen-dec-(map|array).go.tmpl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const genDecMapTmpl = `
 | 
				
			||||||
 | 
					{{var "v"}} := *{{ .Varname }}
 | 
				
			||||||
 | 
					{{var "l"}} := r.ReadMapStart()
 | 
				
			||||||
 | 
					{{var "bh"}} := z.DecBasicHandle()
 | 
				
			||||||
 | 
					if {{var "v"}} == nil {
 | 
				
			||||||
 | 
						{{var "rl"}} := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
 | 
				
			||||||
 | 
						{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
 | 
				
			||||||
 | 
						*{{ .Varname }} = {{var "v"}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					var {{var "mk"}} {{ .KTyp }}
 | 
				
			||||||
 | 
					var {{var "mv"}} {{ .Typ }}
 | 
				
			||||||
 | 
					var {{var "mg"}}, {{var "mdn"}} {{if decElemKindPtr}}, {{var "ms"}}, {{var "mok"}}{{end}} bool
 | 
				
			||||||
 | 
					if {{var "bh"}}.MapValueReset {
 | 
				
			||||||
 | 
						{{if decElemKindPtr}}{{var "mg"}} = true
 | 
				
			||||||
 | 
						{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
 | 
				
			||||||
 | 
						{{else if not decElemKindImmutable}}{{var "mg"}} = true
 | 
				
			||||||
 | 
						{{end}} }
 | 
				
			||||||
 | 
					if {{var "l"}} != 0 {
 | 
				
			||||||
 | 
					{{var "hl"}} := {{var "l"}} > 0 
 | 
				
			||||||
 | 
						for {{var "j"}} := 0; ({{var "hl"}} && {{var "j"}} < {{var "l"}}) || !({{var "hl"}} || r.CheckBreak()); {{var "j"}}++ {
 | 
				
			||||||
 | 
						r.ReadMapElemKey() {{/* z.DecSendContainerState(codecSelfer_containerMapKey{{ .Sfx }}) */}}
 | 
				
			||||||
 | 
						{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
 | 
				
			||||||
 | 
					{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
 | 
				
			||||||
 | 
							{{var "mk"}} = string({{var "bv"}})
 | 
				
			||||||
 | 
						}{{ end }}{{if decElemKindPtr}}
 | 
				
			||||||
 | 
						{{var "ms"}} = true{{end}}
 | 
				
			||||||
 | 
						if {{var "mg"}} {
 | 
				
			||||||
 | 
							{{if decElemKindPtr}}{{var "mv"}}, {{var "mok"}} = {{var "v"}}[{{var "mk"}}] 
 | 
				
			||||||
 | 
							if {{var "mok"}} {
 | 
				
			||||||
 | 
								{{var "ms"}} = false
 | 
				
			||||||
 | 
							} {{else}}{{var "mv"}} = {{var "v"}}[{{var "mk"}}] {{end}}
 | 
				
			||||||
 | 
						} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
 | 
				
			||||||
 | 
						r.ReadMapElemValue() {{/* z.DecSendContainerState(codecSelfer_containerMapValue{{ .Sfx }}) */}}
 | 
				
			||||||
 | 
						{{var "mdn"}} = false
 | 
				
			||||||
 | 
						{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ $y := printf "%vmdn%v" .TempVar .Rand }}{{ decLineVar $x $y }}
 | 
				
			||||||
 | 
						if {{var "mdn"}} {
 | 
				
			||||||
 | 
							if {{ var "bh" }}.DeleteOnNilMapValue { delete({{var "v"}}, {{var "mk"}}) } else { {{var "v"}}[{{var "mk"}}] = {{decElemZero}} }
 | 
				
			||||||
 | 
						} else if {{if decElemKindPtr}} {{var "ms"}} && {{end}} {{var "v"}} != nil {
 | 
				
			||||||
 | 
							{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // else len==0: TODO: Should we clear map entries?
 | 
				
			||||||
 | 
					r.ReadMapEnd() {{/* z.DecSendContainerState(codecSelfer_containerMapEnd{{ .Sfx }}) */}}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const genDecListTmpl = `
 | 
				
			||||||
 | 
					{{var "v"}} := {{if not isArray}}*{{end}}{{ .Varname }}
 | 
				
			||||||
 | 
					{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}{{if not isArray}}
 | 
				
			||||||
 | 
					var {{var "c"}} bool {{/* // changed */}}
 | 
				
			||||||
 | 
					_ = {{var "c"}}{{end}}
 | 
				
			||||||
 | 
					if {{var "l"}} == 0 {
 | 
				
			||||||
 | 
						{{if isSlice }}if {{var "v"}} == nil {
 | 
				
			||||||
 | 
							{{var "v"}} = []{{ .Typ }}{}
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						} else if len({{var "v"}}) != 0 {
 | 
				
			||||||
 | 
							{{var "v"}} = {{var "v"}}[:0]
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						} {{end}} {{if isChan }}if {{var "v"}} == nil {
 | 
				
			||||||
 | 
							{{var "v"}} = make({{ .CTyp }}, 0)
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						} {{end}}
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
						{{var "hl"}} := {{var "l"}} > 0
 | 
				
			||||||
 | 
						var {{var "rl"}} int; _ =  {{var "rl"}}
 | 
				
			||||||
 | 
						{{if isSlice }} if {{var "hl"}} {
 | 
				
			||||||
 | 
						if {{var "l"}} > cap({{var "v"}}) {
 | 
				
			||||||
 | 
							{{var "rl"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
 | 
				
			||||||
 | 
							if {{var "rl"}} <= cap({{var "v"}}) {
 | 
				
			||||||
 | 
								{{var "v"}} = {{var "v"}}[:{{var "rl"}}]
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						} else if {{var "l"}} != len({{var "v"}}) {
 | 
				
			||||||
 | 
							{{var "v"}} = {{var "v"}}[:{{var "l"}}]
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						} {{end}}
 | 
				
			||||||
 | 
						var {{var "j"}} int 
 | 
				
			||||||
 | 
					    // var {{var "dn"}} bool 
 | 
				
			||||||
 | 
						for ; ({{var "hl"}} && {{var "j"}} < {{var "l"}}) || !({{var "hl"}} || r.CheckBreak()); {{var "j"}}++ {
 | 
				
			||||||
 | 
							{{if not isArray}} if {{var "j"}} == 0 && len({{var "v"}}) == 0 {
 | 
				
			||||||
 | 
								if {{var "hl"}} {
 | 
				
			||||||
 | 
									{{var "rl"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									{{var "rl"}} = 8
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
 | 
				
			||||||
 | 
								{{var "c"}} = true 
 | 
				
			||||||
 | 
							}{{end}}
 | 
				
			||||||
 | 
							{{var "h"}}.ElemContainerState({{var "j"}})
 | 
				
			||||||
 | 
					        // {{var "dn"}} = r.TryDecodeAsNil()
 | 
				
			||||||
 | 
					        {{if isChan}}{{ $x := printf "%[1]vv%[2]v" .TempVar .Rand }}var {{var $x}} {{ .Typ }}
 | 
				
			||||||
 | 
							{{ decLineVar $x }}
 | 
				
			||||||
 | 
							{{var "v"}} <- {{ $x }}
 | 
				
			||||||
 | 
					        {{else}}
 | 
				
			||||||
 | 
							// if indefinite, etc, then expand the slice if necessary
 | 
				
			||||||
 | 
							var {{var "db"}} bool
 | 
				
			||||||
 | 
							if {{var "j"}} >= len({{var "v"}}) {
 | 
				
			||||||
 | 
								{{if isSlice }} {{var "v"}} = append({{var "v"}}, {{ zero }}); {{var "c"}} = true
 | 
				
			||||||
 | 
								{{else}} z.DecArrayCannotExpand(len(v), {{var "j"}}+1); {{var "db"}} = true
 | 
				
			||||||
 | 
								{{end}}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if {{var "db"}} {
 | 
				
			||||||
 | 
								z.DecSwallow()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					        {{end}}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						{{if isSlice}} if {{var "j"}} < len({{var "v"}}) {
 | 
				
			||||||
 | 
							{{var "v"}} = {{var "v"}}[:{{var "j"}}]
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						} else if {{var "j"}} == 0 && {{var "v"}} == nil {
 | 
				
			||||||
 | 
							{{var "v"}} = make([]{{ .Typ }}, 0)
 | 
				
			||||||
 | 
							{{var "c"}} = true
 | 
				
			||||||
 | 
						} {{end}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					{{var "h"}}.End()
 | 
				
			||||||
 | 
					{{if not isArray }}if {{var "c"}} { 
 | 
				
			||||||
 | 
						*{{ .Varname }} = {{var "v"}}
 | 
				
			||||||
 | 
					}{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										2014
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2014
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/goversion_arrayof_gte_go15.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/goversion_arrayof_gte_go15.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// +build go1.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const reflectArrayOfSupported = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func reflectArrayOf(count int, elem reflect.Type) reflect.Type {
 | 
				
			||||||
 | 
						return reflect.ArrayOf(count, elem)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/goversion_arrayof_lt_go15.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/coreos/etcd/vendor/github.com/ugorji/go/codec/goversion_arrayof_lt_go15.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT license found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// +build !go1.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const reflectArrayOfSupported = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func reflectArrayOf(count int, elem reflect.Type) reflect.Type {
 | 
				
			||||||
 | 
						panic("codec: reflect.ArrayOf unsupported in this go version")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user