feat: check unused package (#232)
* feat: check unused package update edganiukov/fcm to appleboy/go-fcm * update readme Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * update comment Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
		
							parent
							
								
									77bce18c9f
								
							
						
					
					
						commit
						14dc899b02
					
				|  | @ -20,6 +20,7 @@ pipeline: | |||
|       - make lint | ||||
|       - make build | ||||
|       - make embedmd | ||||
|       - make test-vendor | ||||
|       - coverage all | ||||
|       # send coverage report | ||||
|       - make coverage | ||||
|  |  | |||
							
								
								
									
										14
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										14
									
								
								Makefile
								
								
								
								
							|  | @ -14,6 +14,7 @@ GOFILES := find . -name "*.go" -type f -not -path "./vendor/*" | |||
| SOURCES ?= $(shell find . -name "*.go" -type f) | ||||
| TAGS ?= | ||||
| LDFLAGS ?= -X 'main.Version=$(VERSION)' | ||||
| TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'tempdir') | ||||
| 
 | ||||
| ifneq ($(shell uname), Darwin) | ||||
| 	EXTLDFLAGS = -extldflags "-static" $(null) | ||||
|  | @ -90,6 +91,19 @@ $(EXECUTABLE): $(SOURCES) | |||
| test: fmt-check | ||||
| 	for PKG in $(PACKAGES); do go test -v -cover -coverprofile $$GOPATH/src/$$PKG/coverage.txt $$PKG || exit 1; done; | ||||
| 
 | ||||
| .PHONY: test-vendor | ||||
| test-vendor: | ||||
| 	@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | ||||
| 		go get -u github.com/kardianos/govendor; \
 | ||||
| 	fi | ||||
| 	govendor list +unused | tee "$(TMPDIR)/wc-gitea-unused" | ||||
| 	[ $$(cat "$(TMPDIR)/wc-gitea-unused" | wc -l) -eq 0 ] || echo "Warning: /!\\ Some vendor are not used /!\\" | ||||
| 
 | ||||
| 	govendor list +outside | tee "$(TMPDIR)/wc-gitea-outside" | ||||
| 	[ $$(cat "$(TMPDIR)/wc-gitea-outside" | wc -l) -eq 0 ] || exit 1 | ||||
| 
 | ||||
| 	govendor status || exit 1 | ||||
| 
 | ||||
| redis_test: init | ||||
| 	go test -v -cover ./storage/redis/... | ||||
| 
 | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ A push notification micro server using [Gin](https://github.com/gin-gonic/gin) f | |||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
| * Support [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) using [fcm](https://github.com/edganiukov/fcm) library for Android. | ||||
| * Support [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) using [go-fcm](https://github.com/appleboy/go-fcm) library for Android. | ||||
| * Support [HTTP/2](https://http2.github.io/) Apple Push Notification Service using [apns2](https://github.com/sideshow/apns2) library. | ||||
| * Support [YAML](https://github.com/go-yaml/yaml) configuration. | ||||
| * Support command line to send single Android or iOS notification. | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import ( | |||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/edganiukov/fcm" | ||||
| 	"github.com/appleboy/go-fcm" | ||||
| 	apns "github.com/sideshow/apns2" | ||||
| 	"github.com/sideshow/apns2/certificate" | ||||
| 	"github.com/sideshow/apns2/payload" | ||||
|  | @ -131,7 +131,7 @@ func CheckMessage(req PushNotification) error { | |||
| 		return errors.New(msg) | ||||
| 	} | ||||
| 
 | ||||
| 	// ref: https://developers.google.com/cloud-messaging/http-server-ref
 | ||||
| 	// ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref
 | ||||
| 	if req.Platform == PlatFormAndroid && req.TimeToLive != nil && (*req.TimeToLive < uint(0) || uint(2419200) < *req.TimeToLive) { | ||||
| 		msg = "the message's TimeToLive field must be an integer " + | ||||
| 			"between 0 and 2419200 (4 weeks)" | ||||
|  | @ -456,7 +456,7 @@ Retry: | |||
| 
 | ||||
| // GetAndroidNotification use for define Android notification.
 | ||||
| // HTTP Connection Server Reference for Android
 | ||||
| // https://developers.google.com/cloud-messaging/http-server-ref
 | ||||
| // https://firebase.google.com/docs/cloud-messaging/http-server-ref
 | ||||
| func GetAndroidNotification(req PushNotification) *fcm.Message { | ||||
| 	notification := &fcm.Message{ | ||||
| 		To:                    req.To, | ||||
|  |  | |||
|  | @ -7,9 +7,9 @@ import ( | |||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/appleboy/go-fcm" | ||||
| 	"github.com/appleboy/gorush/config" | ||||
| 	"github.com/buger/jsonparser" | ||||
| 	"github.com/edganiukov/fcm" | ||||
| 	"github.com/sideshow/apns2" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										0
									
								
								vendor/github.com/edganiukov/fcm/LICENSE → vendor/github.com/appleboy/go-fcm/LICENSE
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										0
									
								
								vendor/github.com/edganiukov/fcm/LICENSE → vendor/github.com/appleboy/go-fcm/LICENSE
								
								
									generated
								
								
									vendored
								
								
							
							
								
								
									
										20
									
								
								vendor/github.com/edganiukov/fcm/README.md → vendor/github.com/appleboy/go-fcm/README.md
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										20
									
								
								vendor/github.com/edganiukov/fcm/README.md → vendor/github.com/appleboy/go-fcm/README.md
								
								
									generated
								
								
									vendored
								
								
							|  | @ -1,29 +1,35 @@ | |||
| # fcm | ||||
| # go-fcm | ||||
| 
 | ||||
| [](https://godoc.org/github.com/edganiukov/fcm) | ||||
| [](https://travis-ci.org/edganiukov/fcm) | ||||
| [](https://goreportcard.com/report/github.com/edganiukov/fcm) | ||||
| 
 | ||||
| This project was forked from [github.com/edganiukov/fcmfcm](https://github.com/edganiukov/fcm). | ||||
| 
 | ||||
| Golang client library for Firebase Cloud Messaging. Implemented only [HTTP client](https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream). | ||||
| 
 | ||||
| More information on [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) | ||||
| 
 | ||||
| ### Getting Started | ||||
| ------------------- | ||||
| ## Getting Started | ||||
| 
 | ||||
| To install fcm, use `go get`: | ||||
| 
 | ||||
| ```bash | ||||
| go get github.com/edganiukov/fcm | ||||
| go get github.com/appleboy/go-fcm | ||||
| ``` | ||||
| 
 | ||||
| or `govendor`: | ||||
| 
 | ||||
| ```bash | ||||
| govendor fetch github.com/edganiukov/fcm | ||||
| govendor fetch github.com/appleboy/go-fcm | ||||
| ``` | ||||
| 
 | ||||
| or other tool for vendoring. | ||||
| 
 | ||||
| ### Sample Usage | ||||
| ---------------- | ||||
| ## Sample Usage | ||||
| 
 | ||||
| Here is a simple example illustrating how to use FCM library: | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
|  | @ -65,5 +65,13 @@ func (msg *Message) Validate() error { | |||
| 	if msg.To == "" && (msg.Condition == "" || opCnt > 2) && len(msg.RegistrationIDs) == 0 { | ||||
| 		return ErrInvalidTarget | ||||
| 	} | ||||
| 
 | ||||
| 	if len(msg.RegistrationIDs) > 1000 { | ||||
| 		return ErrToManyRegIDs | ||||
| 	} | ||||
| 
 | ||||
| 	if msg.TimeToLive != nil && *msg.TimeToLive > uint(2419200) { | ||||
| 		return ErrInvalidTimeToLive | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -1,142 +0,0 @@ | |||
| # Backoff | ||||
| 
 | ||||
| A simple exponential backoff counter in Go (Golang) | ||||
| 
 | ||||
| [](https://godoc.org/github.com/jpillora/backoff) [](https://circleci.com/gh/jpillora/backoff) | ||||
| 
 | ||||
| ### Install | ||||
| 
 | ||||
| ``` | ||||
| $ go get -v github.com/jpillora/backoff | ||||
| ``` | ||||
| 
 | ||||
| ### Usage | ||||
| 
 | ||||
| Backoff is a `time.Duration` counter. It starts at `Min`. After every call to `Duration()` it is  multiplied by `Factor`. It is capped at `Max`. It returns to `Min` on every call to `Reset()`. `Jitter` adds randomness ([see below](#example-using-jitter)). Used in conjunction with the `time` package. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| #### Simple example | ||||
| 
 | ||||
| ``` go | ||||
| 
 | ||||
| b := &backoff.Backoff{ | ||||
| 	//These are the defaults | ||||
| 	Min:    100 * time.Millisecond, | ||||
| 	Max:    10 * time.Second, | ||||
| 	Factor: 2, | ||||
| 	Jitter: false, | ||||
| } | ||||
| 
 | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| 
 | ||||
| fmt.Printf("Reset!\n") | ||||
| b.Reset() | ||||
| 
 | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| ``` | ||||
| 
 | ||||
| ``` | ||||
| 100ms | ||||
| 200ms | ||||
| 400ms | ||||
| Reset! | ||||
| 100ms | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| #### Example using `net` package | ||||
| 
 | ||||
| ``` go | ||||
| b := &backoff.Backoff{ | ||||
|     Max:    5 * time.Minute, | ||||
| } | ||||
| 
 | ||||
| for { | ||||
| 	conn, err := net.Dial("tcp", "example.com:5309") | ||||
| 	if err != nil { | ||||
| 		d := b.Duration() | ||||
| 		fmt.Printf("%s, reconnecting in %s", err, d) | ||||
| 		time.Sleep(d) | ||||
| 		continue | ||||
| 	} | ||||
| 	//connected | ||||
| 	b.Reset() | ||||
| 	conn.Write([]byte("hello world!")) | ||||
| 	// ... Read ... Write ... etc | ||||
| 	conn.Close() | ||||
| 	//disconnected | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| #### Example using `Jitter` | ||||
| 
 | ||||
| Enabling `Jitter` adds some randomization to the backoff durations. [See Amazon's writeup of performance gains using jitter](http://www.awsarchitectureblog.com/2015/03/backoff.html). Seeding is not necessary but doing so gives repeatable results. | ||||
| 
 | ||||
| ```go | ||||
| import "math/rand" | ||||
| 
 | ||||
| b := &backoff.Backoff{ | ||||
| 	Jitter: true, | ||||
| } | ||||
| 
 | ||||
| rand.Seed(42) | ||||
| 
 | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| 
 | ||||
| fmt.Printf("Reset!\n") | ||||
| b.Reset() | ||||
| 
 | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| fmt.Printf("%s\n", b.Duration()) | ||||
| ``` | ||||
| 
 | ||||
| ``` | ||||
| 100ms | ||||
| 106.600049ms | ||||
| 281.228155ms | ||||
| Reset! | ||||
| 100ms | ||||
| 104.381845ms | ||||
| 214.957989ms | ||||
| ``` | ||||
| 
 | ||||
| #### Documentation | ||||
| 
 | ||||
| https://godoc.org/github.com/jpillora/backoff | ||||
| 
 | ||||
| #### Credits | ||||
| 
 | ||||
| Ported from some JavaScript written by [@tj](https://github.com/tj) | ||||
| 
 | ||||
| #### MIT License | ||||
| 
 | ||||
| Copyright © 2015 Jaime Pillora <dev@jpillora.com> | ||||
| 
 | ||||
| Permission is hereby granted, free of charge, to any person obtaining | ||||
| a copy of this software and associated documentation files (the | ||||
| 'Software'), to deal in the Software without restriction, including | ||||
| without limitation the rights to use, copy, modify, merge, publish, | ||||
| distribute, sublicense, and/or sell copies of the Software, and to | ||||
| permit persons to whom the Software is furnished to do so, subject to | ||||
| the following conditions: | ||||
| 
 | ||||
| The above copyright notice and this permission notice shall be | ||||
| included in all copies or substantial portions of the Software. | ||||
| 
 | ||||
| THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | ||||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
| IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
| CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
| TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||
| SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  | @ -1,81 +0,0 @@ | |||
| // Package backoff provides an exponential-backoff implementation.
 | ||||
| package backoff | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 	"math/rand" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Backoff is a time.Duration counter, starting at Min. After every call to
 | ||||
| // the Duration method the current timing is multiplied by Factor, but it
 | ||||
| // never exceeds Max.
 | ||||
| //
 | ||||
| // Backoff is not generally concurrent-safe, but the ForAttempt method can
 | ||||
| // be used concurrently.
 | ||||
| type Backoff struct { | ||||
| 	//Factor is the multiplying factor for each increment step
 | ||||
| 	attempt, Factor float64 | ||||
| 	//Jitter eases contention by randomizing backoff steps
 | ||||
| 	Jitter bool | ||||
| 	//Min and Max are the minimum and maximum values of the counter
 | ||||
| 	Min, Max time.Duration | ||||
| } | ||||
| 
 | ||||
| // Duration returns the duration for the current attempt before incrementing
 | ||||
| // the attempt counter. See ForAttempt.
 | ||||
| func (b *Backoff) Duration() time.Duration { | ||||
| 	d := b.ForAttempt(b.attempt) | ||||
| 	b.attempt++ | ||||
| 	return d | ||||
| } | ||||
| 
 | ||||
| // ForAttempt returns the duration for a specific attempt. This is useful if
 | ||||
| // you have a large number of independent Backoffs, but don't want use
 | ||||
| // unnecessary memory storing the Backoff parameters per Backoff. The first
 | ||||
| // attempt should be 0.
 | ||||
| //
 | ||||
| // ForAttempt is concurrent-safe.
 | ||||
| func (b *Backoff) ForAttempt(attempt float64) time.Duration { | ||||
| 	// Zero-values are nonsensical, so we use
 | ||||
| 	// them to apply defaults
 | ||||
| 	min := b.Min | ||||
| 	if min <= 0 { | ||||
| 		min = 100 * time.Millisecond | ||||
| 	} | ||||
| 	max := b.Max | ||||
| 	if max <= 0 { | ||||
| 		max = 10 * time.Second | ||||
| 	} | ||||
| 	if min >= max { | ||||
| 		// short-circuit
 | ||||
| 		return max | ||||
| 	} | ||||
| 
 | ||||
| 	factor := b.Factor | ||||
| 	if factor <= 0 { | ||||
| 		factor = 2 | ||||
| 	} | ||||
| 	//calculate this duration
 | ||||
| 	minf := float64(min) | ||||
| 	durf := minf * math.Pow(factor, attempt) | ||||
| 	if b.Jitter { | ||||
| 		durf = rand.Float64()*(durf-minf) + minf | ||||
| 	} | ||||
| 	dur := time.Duration(durf) | ||||
| 	if dur > max { | ||||
| 		//cap!
 | ||||
| 		return max | ||||
| 	} | ||||
| 	return dur | ||||
| } | ||||
| 
 | ||||
| // Reset restarts the current attempt counter at zero.
 | ||||
| func (b *Backoff) Reset() { | ||||
| 	b.attempt = 0 | ||||
| } | ||||
| 
 | ||||
| // Attempt returns the current attempt counter value.
 | ||||
| func (b *Backoff) Attempt() float64 { | ||||
| 	return b.attempt | ||||
| } | ||||
|  | @ -1,21 +0,0 @@ | |||
| The MIT License (MIT) | ||||
| 
 | ||||
| Copyright (c) 2014 Manuel Martínez-Almeida | ||||
| 
 | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
| 
 | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
|  | @ -1,54 +0,0 @@ | |||
| #Server-Sent Events [](https://godoc.org/github.com/manucorporat/sse) [](https://travis-ci.org/manucorporat/sse) | ||||
| 
 | ||||
| Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/). | ||||
| 
 | ||||
| - [Real world demostration using Gin](http://sse.getgin.io/) | ||||
| - [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/) | ||||
| - [Browser support](http://caniuse.com/#feat=eventsource) | ||||
| 
 | ||||
| ##Sample code | ||||
| 
 | ||||
| ```go | ||||
| import "github.com/manucorporat/sse" | ||||
| 
 | ||||
| func httpHandler(w http.ResponseWriter, req *http.Request) { | ||||
| 	// data can be a primitive like a string, an integer or a float | ||||
| 	sse.Encode(w, sse.Event{ | ||||
| 		Event: "message", | ||||
| 		Data:  "some data\nmore data", | ||||
| 	}) | ||||
| 
 | ||||
| 	// also a complex type, like a map, a struct or a slice | ||||
| 	sse.Encode(w, sse.Event{ | ||||
| 		Id:    "124", | ||||
| 		Event: "message", | ||||
| 		Data: map[string]interface{}{ | ||||
| 			"user":    "manu", | ||||
| 			"date":    time.Now().Unix(), | ||||
| 			"content": "hi!", | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
| ``` | ||||
| ``` | ||||
| event: message | ||||
| data: some data\\nmore data | ||||
| 
 | ||||
| id: 124 | ||||
| event: message | ||||
| data: {"content":"hi!","date":1431540810,"user":"manu"} | ||||
|   | ||||
| ``` | ||||
| 
 | ||||
| ##Content-Type | ||||
| 
 | ||||
| ```go | ||||
| fmt.Println(sse.ContentType) | ||||
| ``` | ||||
| ``` | ||||
| text/event-stream | ||||
| ``` | ||||
| 
 | ||||
| ##Decoding support | ||||
| 
 | ||||
| There is a client-side implementation of SSE coming soon. | ||||
|  | @ -1,116 +0,0 @@ | |||
| // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package sse | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| ) | ||||
| 
 | ||||
| type decoder struct { | ||||
| 	events []Event | ||||
| } | ||||
| 
 | ||||
| func Decode(r io.Reader) ([]Event, error) { | ||||
| 	var dec decoder | ||||
| 	return dec.decode(r) | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) dispatchEvent(event Event, data string) { | ||||
| 	dataLength := len(data) | ||||
| 	if dataLength > 0 { | ||||
| 		//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
 | ||||
| 		data = data[:dataLength-1] | ||||
| 		dataLength-- | ||||
| 	} | ||||
| 	if dataLength == 0 && event.Event == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	if event.Event == "" { | ||||
| 		event.Event = "message" | ||||
| 	} | ||||
| 	event.Data = data | ||||
| 	d.events = append(d.events, event) | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) decode(r io.Reader) ([]Event, error) { | ||||
| 	buf, err := ioutil.ReadAll(r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var currentEvent Event | ||||
| 	var dataBuffer *bytes.Buffer = new(bytes.Buffer) | ||||
| 	// TODO (and unit tests)
 | ||||
| 	// Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
 | ||||
| 	// a single U+000A LINE FEED (LF) character,
 | ||||
| 	// or a single U+000D CARRIAGE RETURN (CR) character.
 | ||||
| 	lines := bytes.Split(buf, []byte{'\n'}) | ||||
| 	for _, line := range lines { | ||||
| 		if len(line) == 0 { | ||||
| 			// If the line is empty (a blank line). Dispatch the event.
 | ||||
| 			d.dispatchEvent(currentEvent, dataBuffer.String()) | ||||
| 
 | ||||
| 			// reset current event and data buffer
 | ||||
| 			currentEvent = Event{} | ||||
| 			dataBuffer.Reset() | ||||
| 			continue | ||||
| 		} | ||||
| 		if line[0] == byte(':') { | ||||
| 			// If the line starts with a U+003A COLON character (:), ignore the line.
 | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		var field, value []byte | ||||
| 		colonIndex := bytes.IndexRune(line, ':') | ||||
| 		if colonIndex != -1 { | ||||
| 			// If the line contains a U+003A COLON character character (:)
 | ||||
| 			// Collect the characters on the line before the first U+003A COLON character (:),
 | ||||
| 			// and let field be that string.
 | ||||
| 			field = line[:colonIndex] | ||||
| 			// Collect the characters on the line after the first U+003A COLON character (:),
 | ||||
| 			// and let value be that string.
 | ||||
| 			value = line[colonIndex+1:] | ||||
| 			// If value starts with a single U+0020 SPACE character, remove it from value.
 | ||||
| 			if len(value) > 0 && value[0] == ' ' { | ||||
| 				value = value[1:] | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
 | ||||
| 			// Use the whole line as the field name, and the empty string as the field value.
 | ||||
| 			field = line | ||||
| 			value = []byte{} | ||||
| 		} | ||||
| 		// The steps to process the field given a field name and a field value depend on the field name,
 | ||||
| 		// as given in the following list. Field names must be compared literally,
 | ||||
| 		// with no case folding performed.
 | ||||
| 		switch string(field) { | ||||
| 		case "event": | ||||
| 			// Set the event name buffer to field value.
 | ||||
| 			currentEvent.Event = string(value) | ||||
| 		case "id": | ||||
| 			// Set the event stream's last event ID to the field value.
 | ||||
| 			currentEvent.Id = string(value) | ||||
| 		case "retry": | ||||
| 			// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
 | ||||
| 			// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
 | ||||
| 			// Otherwise, ignore the field.
 | ||||
| 			currentEvent.Id = string(value) | ||||
| 		case "data": | ||||
| 			// Append the field value to the data buffer,
 | ||||
| 			dataBuffer.Write(value) | ||||
| 			// then append a single U+000A LINE FEED (LF) character to the data buffer.
 | ||||
| 			dataBuffer.WriteString("\n") | ||||
| 		default: | ||||
| 			//Otherwise. The field is ignored.
 | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| 	// Once the end of the file is reached, the user agent must dispatch the event one final time.
 | ||||
| 	d.dispatchEvent(currentEvent, dataBuffer.String()) | ||||
| 
 | ||||
| 	return d.events, nil | ||||
| } | ||||
|  | @ -1,106 +0,0 @@ | |||
| // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package sse | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Server-Sent Events
 | ||||
| // W3C Working Draft 29 October 2009
 | ||||
| // http://www.w3.org/TR/2009/WD-eventsource-20091029/
 | ||||
| 
 | ||||
| const ContentType = "text/event-stream" | ||||
| 
 | ||||
| var contentType = []string{ContentType} | ||||
| var noCache = []string{"no-cache"} | ||||
| 
 | ||||
| var fieldReplacer = strings.NewReplacer( | ||||
| 	"\n", "\\n", | ||||
| 	"\r", "\\r") | ||||
| 
 | ||||
| var dataReplacer = strings.NewReplacer( | ||||
| 	"\n", "\ndata:", | ||||
| 	"\r", "\\r") | ||||
| 
 | ||||
| type Event struct { | ||||
| 	Event string | ||||
| 	Id    string | ||||
| 	Retry uint | ||||
| 	Data  interface{} | ||||
| } | ||||
| 
 | ||||
| func Encode(writer io.Writer, event Event) error { | ||||
| 	w := checkWriter(writer) | ||||
| 	writeId(w, event.Id) | ||||
| 	writeEvent(w, event.Event) | ||||
| 	writeRetry(w, event.Retry) | ||||
| 	return writeData(w, event.Data) | ||||
| } | ||||
| 
 | ||||
| func writeId(w stringWriter, id string) { | ||||
| 	if len(id) > 0 { | ||||
| 		w.WriteString("id:") | ||||
| 		fieldReplacer.WriteString(w, id) | ||||
| 		w.WriteString("\n") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func writeEvent(w stringWriter, event string) { | ||||
| 	if len(event) > 0 { | ||||
| 		w.WriteString("event:") | ||||
| 		fieldReplacer.WriteString(w, event) | ||||
| 		w.WriteString("\n") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func writeRetry(w stringWriter, retry uint) { | ||||
| 	if retry > 0 { | ||||
| 		w.WriteString("retry:") | ||||
| 		w.WriteString(strconv.FormatUint(uint64(retry), 10)) | ||||
| 		w.WriteString("\n") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func writeData(w stringWriter, data interface{}) error { | ||||
| 	w.WriteString("data:") | ||||
| 	switch kindOfData(data) { | ||||
| 	case reflect.Struct, reflect.Slice, reflect.Map: | ||||
| 		err := json.NewEncoder(w).Encode(data) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		w.WriteString("\n") | ||||
| 	default: | ||||
| 		dataReplacer.WriteString(w, fmt.Sprint(data)) | ||||
| 		w.WriteString("\n\n") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (r Event) Render(w http.ResponseWriter) error { | ||||
| 	header := w.Header() | ||||
| 	header["Content-Type"] = contentType | ||||
| 
 | ||||
| 	if _, exist := header["Cache-Control"]; !exist { | ||||
| 		header["Cache-Control"] = noCache | ||||
| 	} | ||||
| 	return Encode(w, r) | ||||
| } | ||||
| 
 | ||||
| func kindOfData(data interface{}) reflect.Kind { | ||||
| 	value := reflect.ValueOf(data) | ||||
| 	valueType := value.Kind() | ||||
| 	if valueType == reflect.Ptr { | ||||
| 		valueType = value.Elem().Kind() | ||||
| 	} | ||||
| 	return valueType | ||||
| } | ||||
|  | @ -1,24 +0,0 @@ | |||
| package sse | ||||
| 
 | ||||
| import "io" | ||||
| 
 | ||||
| type stringWriter interface { | ||||
| 	io.Writer | ||||
| 	WriteString(string) (int, error) | ||||
| } | ||||
| 
 | ||||
| type stringWrapper struct { | ||||
| 	io.Writer | ||||
| } | ||||
| 
 | ||||
| func (w stringWrapper) WriteString(str string) (int, error) { | ||||
| 	return w.Writer.Write([]byte(str)) | ||||
| } | ||||
| 
 | ||||
| func checkWriter(writer io.Writer) stringWriter { | ||||
| 	if w, ok := writer.(stringWriter); ok { | ||||
| 		return w | ||||
| 	} else { | ||||
| 		return stringWrapper{writer} | ||||
| 	} | ||||
| } | ||||
|  | @ -1,27 +0,0 @@ | |||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | @ -1,4 +0,0 @@ | |||
| go-xmpp | ||||
| ======= | ||||
| 
 | ||||
| go xmpp library (original was written by russ cox  ) | ||||
|  | @ -1,929 +0,0 @@ | |||
| // 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.
 | ||||
| 
 | ||||
| // TODO(rsc):
 | ||||
| //	More precise error handling.
 | ||||
| //	Presence functionality.
 | ||||
| // TODO(mattn):
 | ||||
| //  Add proxy authentication.
 | ||||
| 
 | ||||
| // Package xmpp implements a simple Google Talk client
 | ||||
| // using the XMPP protocol described in RFC 3920 and RFC 3921.
 | ||||
| package xmpp | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"encoding/xml" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	nsStream  = "http://etherx.jabber.org/streams" | ||||
| 	nsTLS     = "urn:ietf:params:xml:ns:xmpp-tls" | ||||
| 	nsSASL    = "urn:ietf:params:xml:ns:xmpp-sasl" | ||||
| 	nsBind    = "urn:ietf:params:xml:ns:xmpp-bind" | ||||
| 	nsClient  = "jabber:client" | ||||
| 	nsSession = "urn:ietf:params:xml:ns:xmpp-session" | ||||
| ) | ||||
| 
 | ||||
| // Default TLS configuration options
 | ||||
| var DefaultConfig tls.Config | ||||
| 
 | ||||
| // Cookie is a unique XMPP session identifier
 | ||||
| type Cookie uint64 | ||||
| 
 | ||||
| func getCookie() Cookie { | ||||
| 	var buf [8]byte | ||||
| 	if _, err := rand.Reader.Read(buf[:]); err != nil { | ||||
| 		panic("Failed to read random bytes: " + err.Error()) | ||||
| 	} | ||||
| 	return Cookie(binary.LittleEndian.Uint64(buf[:])) | ||||
| } | ||||
| 
 | ||||
| // Client holds XMPP connection opitons
 | ||||
| type Client struct { | ||||
| 	conn   net.Conn // connection to server
 | ||||
| 	jid    string   // Jabber ID for our connection
 | ||||
| 	domain string | ||||
| 	p      *xml.Decoder | ||||
| } | ||||
| 
 | ||||
| func (c *Client) JID() string { | ||||
| 	return c.jid | ||||
| } | ||||
| 
 | ||||
| func connect(host, user, passwd string) (net.Conn, error) { | ||||
| 	addr := host | ||||
| 
 | ||||
| 	if strings.TrimSpace(host) == "" { | ||||
| 		a := strings.SplitN(user, "@", 2) | ||||
| 		if len(a) == 2 { | ||||
| 			addr = a[1] | ||||
| 		} | ||||
| 	} | ||||
| 	a := strings.SplitN(host, ":", 2) | ||||
| 	if len(a) == 1 { | ||||
| 		addr += ":5222" | ||||
| 	} | ||||
| 	proxy := os.Getenv("HTTP_PROXY") | ||||
| 	if proxy == "" { | ||||
| 		proxy = os.Getenv("http_proxy") | ||||
| 	} | ||||
| 	if proxy != "" { | ||||
| 		url, err := url.Parse(proxy) | ||||
| 		if err == nil { | ||||
| 			addr = url.Host | ||||
| 		} | ||||
| 	} | ||||
| 	c, err := net.Dial("tcp", addr) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if proxy != "" { | ||||
| 		fmt.Fprintf(c, "CONNECT %s HTTP/1.1\r\n", host) | ||||
| 		fmt.Fprintf(c, "Host: %s\r\n", host) | ||||
| 		fmt.Fprintf(c, "\r\n") | ||||
| 		br := bufio.NewReader(c) | ||||
| 		req, _ := http.NewRequest("CONNECT", host, nil) | ||||
| 		resp, err := http.ReadResponse(br, req) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if resp.StatusCode != 200 { | ||||
| 			f := strings.SplitN(resp.Status, " ", 2) | ||||
| 			return nil, errors.New(f[1]) | ||||
| 		} | ||||
| 	} | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| // Options are used to specify additional options for new clients, such as a Resource.
 | ||||
| type Options struct { | ||||
| 	// Host specifies what host to connect to, as either "hostname" or "hostname:port"
 | ||||
| 	// If host is not specified, the  DNS SRV should be used to find the host from the domainpart of the JID.
 | ||||
| 	// Default the port to 5222.
 | ||||
| 	Host string | ||||
| 
 | ||||
| 	// User specifies what user to authenticate to the remote server.
 | ||||
| 	User string | ||||
| 
 | ||||
| 	// Password supplies the password to use for authentication with the remote server.
 | ||||
| 	Password string | ||||
| 
 | ||||
| 	// Resource specifies an XMPP client resource, like "bot", instead of accepting one
 | ||||
| 	// from the server.  Use "" to let the server generate one for your client.
 | ||||
| 	Resource string | ||||
| 
 | ||||
| 	// OAuthScope provides go-xmpp the required scope for OAuth2 authentication.
 | ||||
| 	OAuthScope string | ||||
| 
 | ||||
| 	// OAuthToken provides go-xmpp with the required OAuth2 token used to authenticate
 | ||||
| 	OAuthToken string | ||||
| 
 | ||||
| 	// OAuthXmlNs provides go-xmpp with the required namespaced used for OAuth2 authentication.  This is
 | ||||
| 	// provided to the server as the xmlns:auth attribute of the OAuth2 authentication request.
 | ||||
| 	OAuthXmlNs string | ||||
| 
 | ||||
| 	// TLS Config
 | ||||
| 	TLSConfig *tls.Config | ||||
| 
 | ||||
| 	// InsecureAllowUnencryptedAuth permits authentication over a TCP connection that has not been promoted to
 | ||||
| 	// TLS by STARTTLS; this could leak authentication information over the network, or permit man in the middle
 | ||||
| 	// attacks.
 | ||||
| 	InsecureAllowUnencryptedAuth bool | ||||
| 
 | ||||
| 	// NoTLS directs go-xmpp to not use TLS initially to contact the server; instead, a plain old unencrypted
 | ||||
| 	// TCP connection should be used. (Can be combined with StartTLS to support STARTTLS-based servers.)
 | ||||
| 	NoTLS bool | ||||
| 
 | ||||
| 	// StartTLS directs go-xmpp to STARTTLS if the server supports it; go-xmpp will automatically STARTTLS
 | ||||
| 	// if the server requires it regardless of this option.
 | ||||
| 	StartTLS bool | ||||
| 
 | ||||
| 	// Debug output
 | ||||
| 	Debug bool | ||||
| 
 | ||||
| 	// Use server sessions
 | ||||
| 	Session bool | ||||
| 
 | ||||
| 	// Presence Status
 | ||||
| 	Status string | ||||
| 
 | ||||
| 	// Status message
 | ||||
| 	StatusMessage string | ||||
| } | ||||
| 
 | ||||
| // NewClient establishes a new Client connection based on a set of Options.
 | ||||
| func (o Options) NewClient() (*Client, error) { | ||||
| 	host := o.Host | ||||
| 	c, err := connect(host, o.User, o.Password) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if strings.LastIndex(o.Host, ":") > 0 { | ||||
| 		host = host[:strings.LastIndex(o.Host, ":")] | ||||
| 	} | ||||
| 
 | ||||
| 	client := new(Client) | ||||
| 	if o.NoTLS { | ||||
| 		client.conn = c | ||||
| 	} else { | ||||
| 		var tlsconn *tls.Conn | ||||
| 		if o.TLSConfig != nil { | ||||
| 			tlsconn = tls.Client(c, o.TLSConfig) | ||||
| 		} else { | ||||
| 			DefaultConfig.ServerName = host | ||||
| 			tlsconn = tls.Client(c, &DefaultConfig) | ||||
| 		} | ||||
| 		if err = tlsconn.Handshake(); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		insecureSkipVerify := DefaultConfig.InsecureSkipVerify | ||||
| 		if o.TLSConfig != nil { | ||||
| 			insecureSkipVerify = o.TLSConfig.InsecureSkipVerify | ||||
| 		} | ||||
| 		if !insecureSkipVerify { | ||||
| 			if err = tlsconn.VerifyHostname(host); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		client.conn = tlsconn | ||||
| 	} | ||||
| 
 | ||||
| 	if err := client.init(&o); err != nil { | ||||
| 		client.Close() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return client, nil | ||||
| } | ||||
| 
 | ||||
| // NewClient creates a new connection to a host given as "hostname" or "hostname:port".
 | ||||
| // If host is not specified, the  DNS SRV should be used to find the host from the domainpart of the JID.
 | ||||
| // Default the port to 5222.
 | ||||
| func NewClient(host, user, passwd string, debug bool) (*Client, error) { | ||||
| 	opts := Options{ | ||||
| 		Host:     host, | ||||
| 		User:     user, | ||||
| 		Password: passwd, | ||||
| 		Debug:    debug, | ||||
| 		Session:  false, | ||||
| 	} | ||||
| 	return opts.NewClient() | ||||
| } | ||||
| 
 | ||||
| // NewClientNoTLS creates a new client without TLS
 | ||||
| func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) { | ||||
| 	opts := Options{ | ||||
| 		Host:     host, | ||||
| 		User:     user, | ||||
| 		Password: passwd, | ||||
| 		NoTLS:    true, | ||||
| 		Debug:    debug, | ||||
| 		Session:  false, | ||||
| 	} | ||||
| 	return opts.NewClient() | ||||
| } | ||||
| 
 | ||||
| // Close closes the XMPP connection
 | ||||
| func (c *Client) Close() error { | ||||
| 	if c.conn != (*tls.Conn)(nil) { | ||||
| 		return c.conn.Close() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string { | ||||
| 	h := func(text string) []byte { | ||||
| 		h := md5.New() | ||||
| 		h.Write([]byte(text)) | ||||
| 		return h.Sum(nil) | ||||
| 	} | ||||
| 	hex := func(bytes []byte) string { | ||||
| 		return fmt.Sprintf("%x", bytes) | ||||
| 	} | ||||
| 	kd := func(secret, data string) []byte { | ||||
| 		return h(secret + ":" + data) | ||||
| 	} | ||||
| 
 | ||||
| 	a1 := string(h(username+":"+realm+":"+passwd)) + ":" + nonce + ":" + cnonceStr | ||||
| 	a2 := authenticate + ":" + digestURI | ||||
| 	response := hex(kd(hex(h(a1)), nonce+":"+nonceCountStr+":"+cnonceStr+":auth:"+hex(h(a2)))) | ||||
| 	return response | ||||
| } | ||||
| 
 | ||||
| func cnonce() string { | ||||
| 	randSize := big.NewInt(0) | ||||
| 	randSize.Lsh(big.NewInt(1), 64) | ||||
| 	cn, err := rand.Int(rand.Reader, randSize) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return fmt.Sprintf("%016x", cn) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) init(o *Options) error { | ||||
| 
 | ||||
| 	var domain string | ||||
| 	var user string | ||||
| 	a := strings.SplitN(o.User, "@", 2) | ||||
| 	if len(o.User) > 0 { | ||||
| 		if len(a) != 2 { | ||||
| 			return errors.New("xmpp: invalid username (want user@domain): " + o.User) | ||||
| 		} | ||||
| 		user = a[0] | ||||
| 		domain = a[1] | ||||
| 	} // Otherwise, we'll be attempting ANONYMOUS
 | ||||
| 
 | ||||
| 	// Declare intent to be a jabber client and gather stream features.
 | ||||
| 	f, err := c.startStream(o, domain) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// If the server requires we STARTTLS, attempt to do so.
 | ||||
| 	if f, err = c.startTLSIfRequired(f, o, domain); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if o.User == "" && o.Password == "" { | ||||
| 		foundAnonymous := false | ||||
| 		for _, m := range f.Mechanisms.Mechanism { | ||||
| 			if m == "ANONYMOUS" { | ||||
| 				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL) | ||||
| 				foundAnonymous = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !foundAnonymous { | ||||
| 			return fmt.Errorf("ANONYMOUS authentication is not an option and username and password were not specified") | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Even digest forms of authentication are unsafe if we do not know that the host
 | ||||
| 		// we are talking to is the actual server, and not a man in the middle playing
 | ||||
| 		// proxy.
 | ||||
| 		if !c.IsEncrypted() && !o.InsecureAllowUnencryptedAuth { | ||||
| 			return errors.New("refusing to authenticate over unencrypted TCP connection") | ||||
| 		} | ||||
| 
 | ||||
| 		mechanism := "" | ||||
| 		for _, m := range f.Mechanisms.Mechanism { | ||||
| 			if m == "X-OAUTH2" && o.OAuthToken != "" && o.OAuthScope != "" { | ||||
| 				mechanism = m | ||||
| 				// Oauth authentication: send base64-encoded \x00 user \x00 token.
 | ||||
| 				raw := "\x00" + user + "\x00" + o.OAuthToken | ||||
| 				enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw))) | ||||
| 				base64.StdEncoding.Encode(enc, []byte(raw)) | ||||
| 				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='X-OAUTH2' auth:service='oauth2' "+ | ||||
| 					"xmlns:auth='%s'>%s</auth>\n", nsSASL, o.OAuthXmlNs, enc) | ||||
| 				break | ||||
| 			} | ||||
| 			if m == "PLAIN" { | ||||
| 				mechanism = m | ||||
| 				// Plain authentication: send base64-encoded \x00 user \x00 password.
 | ||||
| 				raw := "\x00" + user + "\x00" + o.Password | ||||
| 				enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw))) | ||||
| 				base64.StdEncoding.Encode(enc, []byte(raw)) | ||||
| 				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", nsSASL, enc) | ||||
| 				break | ||||
| 			} | ||||
| 			if m == "DIGEST-MD5" { | ||||
| 				mechanism = m | ||||
| 				// Digest-MD5 authentication
 | ||||
| 				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL) | ||||
| 				var ch saslChallenge | ||||
| 				if err = c.p.DecodeElement(&ch, nil); err != nil { | ||||
| 					return errors.New("unmarshal <challenge>: " + err.Error()) | ||||
| 				} | ||||
| 				b, err := base64.StdEncoding.DecodeString(string(ch)) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				tokens := map[string]string{} | ||||
| 				for _, token := range strings.Split(string(b), ",") { | ||||
| 					kv := strings.SplitN(strings.TrimSpace(token), "=", 2) | ||||
| 					if len(kv) == 2 { | ||||
| 						if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' { | ||||
| 							kv[1] = kv[1][1 : len(kv[1])-1] | ||||
| 						} | ||||
| 						tokens[kv[0]] = kv[1] | ||||
| 					} | ||||
| 				} | ||||
| 				realm, _ := tokens["realm"] | ||||
| 				nonce, _ := tokens["nonce"] | ||||
| 				qop, _ := tokens["qop"] | ||||
| 				charset, _ := tokens["charset"] | ||||
| 				cnonceStr := cnonce() | ||||
| 				digestURI := "xmpp/" + domain | ||||
| 				nonceCount := fmt.Sprintf("%08x", 1) | ||||
| 				digest := saslDigestResponse(user, realm, o.Password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount) | ||||
| 				message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr + | ||||
| 					"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset | ||||
| 
 | ||||
| 				fmt.Fprintf(c.conn, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message))) | ||||
| 
 | ||||
| 				var rspauth saslRspAuth | ||||
| 				if err = c.p.DecodeElement(&rspauth, nil); err != nil { | ||||
| 					return errors.New("unmarshal <challenge>: " + err.Error()) | ||||
| 				} | ||||
| 				b, err = base64.StdEncoding.DecodeString(string(rspauth)) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				fmt.Fprintf(c.conn, "<response xmlns='%s'/>\n", nsSASL) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if mechanism == "" { | ||||
| 			return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism) | ||||
| 		} | ||||
| 	} | ||||
| 	// Next message should be either success or failure.
 | ||||
| 	name, val, err := next(c.p) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	switch v := val.(type) { | ||||
| 	case *saslSuccess: | ||||
| 	case *saslFailure: | ||||
| 		errorMessage := v.Text | ||||
| 		if errorMessage == "" { | ||||
| 			// v.Any is type of sub-element in failure,
 | ||||
| 			// which gives a description of what failed if there was no text element
 | ||||
| 			errorMessage = v.Any.Local | ||||
| 		} | ||||
| 		return errors.New("auth failure: " + errorMessage) | ||||
| 	default: | ||||
| 		return errors.New("expected <success> or <failure>, got <" + name.Local + "> in " + name.Space) | ||||
| 	} | ||||
| 
 | ||||
| 	// Now that we're authenticated, we're supposed to start the stream over again.
 | ||||
| 	// Declare intent to be a jabber client.
 | ||||
| 	if f, err = c.startStream(o, domain); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate a unique cookie
 | ||||
| 	cookie := getCookie() | ||||
| 
 | ||||
| 	// Send IQ message asking to bind to the local user name.
 | ||||
| 	if o.Resource == "" { | ||||
| 		fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'></bind></iq>\n", cookie, nsBind) | ||||
| 	} else { | ||||
| 		fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource) | ||||
| 	} | ||||
| 	var iq clientIQ | ||||
| 	if err = c.p.DecodeElement(&iq, nil); err != nil { | ||||
| 		return errors.New("unmarshal <iq>: " + err.Error()) | ||||
| 	} | ||||
| 	if &iq.Bind == nil { | ||||
| 		return errors.New("<iq> result missing <bind>") | ||||
| 	} | ||||
| 	c.jid = iq.Bind.Jid // our local id
 | ||||
| 	c.domain = domain | ||||
| 
 | ||||
| 	if o.Session { | ||||
| 		//if server support session, open it
 | ||||
| 		fmt.Fprintf(c.conn, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>", xmlEscape(domain), cookie, nsSession) | ||||
| 	} | ||||
| 
 | ||||
| 	// We're connected and can now receive and send messages.
 | ||||
| 	fmt.Fprintf(c.conn, "<presence xml:lang='en'><show>%s</show><status>%s</status></presence>", o.Status, o.StatusMessage) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // startTlsIfRequired examines the server's stream features and, if STARTTLS is required or supported, performs the TLS handshake.
 | ||||
| // f will be updated if the handshake completes, as the new stream's features are typically different from the original.
 | ||||
| func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string) (*streamFeatures, error) { | ||||
| 	// whether we start tls is a matter of opinion: the server's and the user's.
 | ||||
| 	switch { | ||||
| 	case f.StartTLS == nil: | ||||
| 		// the server does not support STARTTLS
 | ||||
| 		return f, nil | ||||
| 	case f.StartTLS.Required != nil: | ||||
| 		// the server requires STARTTLS.
 | ||||
| 	case !o.StartTLS: | ||||
| 		// the user wants STARTTLS and the server supports it.
 | ||||
| 	} | ||||
| 	var err error | ||||
| 
 | ||||
| 	fmt.Fprintf(c.conn, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n") | ||||
| 	var k tlsProceed | ||||
| 	if err = c.p.DecodeElement(&k, nil); err != nil { | ||||
| 		return f, errors.New("unmarshal <proceed>: " + err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	tc := o.TLSConfig | ||||
| 	if tc == nil { | ||||
| 		tc = new(tls.Config) | ||||
| 		*tc = DefaultConfig | ||||
| 		//TODO(scott): we should consider using the server's address or reverse lookup
 | ||||
| 		tc.ServerName = domain | ||||
| 	} | ||||
| 	t := tls.Client(c.conn, tc) | ||||
| 
 | ||||
| 	if err = t.Handshake(); err != nil { | ||||
| 		return f, errors.New("starttls handshake: " + err.Error()) | ||||
| 	} | ||||
| 	c.conn = t | ||||
| 
 | ||||
| 	// restart our declaration of XMPP stream intentions.
 | ||||
| 	tf, err := c.startStream(o, domain) | ||||
| 	if err != nil { | ||||
| 		return f, err | ||||
| 	} | ||||
| 	return tf, nil | ||||
| } | ||||
| 
 | ||||
| // startStream will start a new XML decoder for the connection, signal the start of a stream to the server and verify that the server has
 | ||||
| // also started the stream; if o.Debug is true, startStream will tee decoded XML data to stderr.  The features advertised by the server
 | ||||
| // will be returned.
 | ||||
| func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) { | ||||
| 	if o.Debug { | ||||
| 		c.p = xml.NewDecoder(tee{c.conn, os.Stderr}) | ||||
| 	} else { | ||||
| 		c.p = xml.NewDecoder(c.conn) | ||||
| 	} | ||||
| 
 | ||||
| 	_, err := fmt.Fprintf(c.conn, "<?xml version='1.0'?>\n"+ | ||||
| 		"<stream:stream to='%s' xmlns='%s'\n"+ | ||||
| 		" xmlns:stream='%s' version='1.0'>\n", | ||||
| 		xmlEscape(domain), nsClient, nsStream) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// We expect the server to start a <stream>.
 | ||||
| 	se, err := nextStart(c.p) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if se.Name.Space != nsStream || se.Name.Local != "stream" { | ||||
| 		return nil, fmt.Errorf("expected <stream> but got <%v> in %v", se.Name.Local, se.Name.Space) | ||||
| 	} | ||||
| 
 | ||||
| 	// Now we're in the stream and can use Unmarshal.
 | ||||
| 	// Next message should be <features> to tell us authentication options.
 | ||||
| 	// See section 4.6 in RFC 3920.
 | ||||
| 	f := new(streamFeatures) | ||||
| 	if err = c.p.DecodeElement(f, nil); err != nil { | ||||
| 		return f, errors.New("unmarshal <features>: " + err.Error()) | ||||
| 	} | ||||
| 	return f, nil | ||||
| } | ||||
| 
 | ||||
| // IsEncrypted will return true if the client is connected using a TLS transport, either because it used.
 | ||||
| // TLS to connect from the outset, or because it successfully used STARTTLS to promote a TCP connection to TLS.
 | ||||
| func (c *Client) IsEncrypted() bool { | ||||
| 	_, ok := c.conn.(*tls.Conn) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // Chat is an incoming or outgoing XMPP chat message.
 | ||||
| type Chat struct { | ||||
| 	Remote    string | ||||
| 	Type      string | ||||
| 	Text      string | ||||
| 	Roster    Roster | ||||
| 	Other     []string | ||||
| 	OtherElem []XMLElement | ||||
| 	Stamp     time.Time | ||||
| } | ||||
| 
 | ||||
| type Roster []Contact | ||||
| 
 | ||||
| type Contact struct { | ||||
| 	Remote string | ||||
| 	Name   string | ||||
| 	Group  []string | ||||
| } | ||||
| 
 | ||||
| // Presence is an XMPP presence notification.
 | ||||
| type Presence struct { | ||||
| 	From   string | ||||
| 	To     string | ||||
| 	Type   string | ||||
| 	Show   string | ||||
| 	Status string | ||||
| } | ||||
| 
 | ||||
| type IQ struct { | ||||
| 	ID    string | ||||
| 	From  string | ||||
| 	To    string | ||||
| 	Type  string | ||||
| 	Query []byte | ||||
| } | ||||
| 
 | ||||
| // Recv waits to receive the next XMPP stanza.
 | ||||
| // Return type is either a presence notification or a chat message.
 | ||||
| func (c *Client) Recv() (stanza interface{}, err error) { | ||||
| 	for { | ||||
| 		_, val, err := next(c.p) | ||||
| 		if err != nil { | ||||
| 			return Chat{}, err | ||||
| 		} | ||||
| 		switch v := val.(type) { | ||||
| 		case *clientMessage: | ||||
| 			stamp, _ := time.Parse( | ||||
| 				"2006-01-02T15:04:05Z", | ||||
| 				v.Delay.Stamp, | ||||
| 			) | ||||
| 			chat := Chat{ | ||||
| 				Remote:    v.From, | ||||
| 				Type:      v.Type, | ||||
| 				Text:      v.Body, | ||||
| 				Other:     v.OtherStrings(), | ||||
| 				OtherElem: v.Other, | ||||
| 				Stamp:     stamp, | ||||
| 			} | ||||
| 			return chat, nil | ||||
| 		case *clientQuery: | ||||
| 			var r Roster | ||||
| 			for _, item := range v.Item { | ||||
| 				r = append(r, Contact{item.Jid, item.Name, item.Group}) | ||||
| 			} | ||||
| 			return Chat{Type: "roster", Roster: r}, nil | ||||
| 		case *clientPresence: | ||||
| 			return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil | ||||
| 		case *clientIQ: | ||||
| 			if bytes.Equal(v.Query, []byte(`<ping xmlns='urn:xmpp:ping'/>`)) { | ||||
| 				err := c.SendResultPing(v.ID, v.From) | ||||
| 				if err != nil { | ||||
| 					return Chat{}, err | ||||
| 				} | ||||
| 			} | ||||
| 			return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type, Query: v.Query}, nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Send sends the message wrapped inside an XMPP message stanza body.
 | ||||
| func (c *Client) Send(chat Chat) (n int, err error) { | ||||
| 	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<body>%s</body></message>", | ||||
| 		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text)) | ||||
| } | ||||
| 
 | ||||
| // SendOrg sends the original text without being wrapped in an XMPP message stanza.
 | ||||
| func (c *Client) SendOrg(org string) (n int, err error) { | ||||
| 	return fmt.Fprint(c.conn, org) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) SendPresence(presence Presence) (n int, err error) { | ||||
| 	return fmt.Fprintf(c.conn, "<presence from='%s' to='%s'/>", xmlEscape(presence.From), xmlEscape(presence.To)) | ||||
| } | ||||
| 
 | ||||
| // SendHtml sends the message as HTML as defined by XEP-0071
 | ||||
| func (c *Client) SendHtml(chat Chat) (n int, err error) { | ||||
| 	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+ | ||||
| 		"<body>%s</body>"+ | ||||
| 		"<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html></message>", | ||||
| 		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text) | ||||
| } | ||||
| 
 | ||||
| // Roster asks for the chat roster.
 | ||||
| func (c *Client) Roster() error { | ||||
| 	fmt.Fprintf(c.conn, "<iq from='%s' type='get' id='roster1'><query xmlns='jabber:iq:roster'/></iq>\n", xmlEscape(c.jid)) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RFC 3920  C.1  Streams name space
 | ||||
| type streamFeatures struct { | ||||
| 	XMLName    xml.Name `xml:"http://etherx.jabber.org/streams features"` | ||||
| 	StartTLS   *tlsStartTLS | ||||
| 	Mechanisms saslMechanisms | ||||
| 	Bind       bindBind | ||||
| 	Session    bool | ||||
| } | ||||
| 
 | ||||
| type streamError struct { | ||||
| 	XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"` | ||||
| 	Any     xml.Name | ||||
| 	Text    string | ||||
| } | ||||
| 
 | ||||
| // RFC 3920  C.3  TLS name space
 | ||||
| type tlsStartTLS struct { | ||||
| 	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"` | ||||
| 	Required *string  `xml:"required"` | ||||
| } | ||||
| 
 | ||||
| type tlsProceed struct { | ||||
| 	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"` | ||||
| } | ||||
| 
 | ||||
| type tlsFailure struct { | ||||
| 	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls failure"` | ||||
| } | ||||
| 
 | ||||
| // RFC 3920  C.4  SASL name space
 | ||||
| type saslMechanisms struct { | ||||
| 	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"` | ||||
| 	Mechanism []string `xml:"mechanism"` | ||||
| } | ||||
| 
 | ||||
| type saslAuth struct { | ||||
| 	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"` | ||||
| 	Mechanism string   `xml:",attr"` | ||||
| } | ||||
| 
 | ||||
| type saslChallenge string | ||||
| 
 | ||||
| type saslRspAuth string | ||||
| 
 | ||||
| type saslResponse string | ||||
| 
 | ||||
| type saslAbort struct { | ||||
| 	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl abort"` | ||||
| } | ||||
| 
 | ||||
| type saslSuccess struct { | ||||
| 	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl success"` | ||||
| } | ||||
| 
 | ||||
| type saslFailure struct { | ||||
| 	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"` | ||||
| 	Any     xml.Name `xml:",any"` | ||||
| 	Text    string   `xml:"text"` | ||||
| } | ||||
| 
 | ||||
| // RFC 3920  C.5  Resource binding name space
 | ||||
| type bindBind struct { | ||||
| 	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"` | ||||
| 	Resource string | ||||
| 	Jid      string `xml:"jid"` | ||||
| } | ||||
| 
 | ||||
| // RFC 3921  B.1  jabber:client
 | ||||
| type clientMessage struct { | ||||
| 	XMLName xml.Name `xml:"jabber:client message"` | ||||
| 	From    string   `xml:"from,attr"` | ||||
| 	ID      string   `xml:"id,attr"` | ||||
| 	To      string   `xml:"to,attr"` | ||||
| 	Type    string   `xml:"type,attr"` // chat, error, groupchat, headline, or normal
 | ||||
| 
 | ||||
| 	// These should technically be []clientText, but string is much more convenient.
 | ||||
| 	Subject string `xml:"subject"` | ||||
| 	Body    string `xml:"body"` | ||||
| 	Thread  string `xml:"thread"` | ||||
| 
 | ||||
| 	// Any hasn't matched element
 | ||||
| 	Other []XMLElement `xml:",any"` | ||||
| 
 | ||||
| 	Delay Delay `xml:"delay"` | ||||
| } | ||||
| 
 | ||||
| func (m *clientMessage) OtherStrings() []string { | ||||
| 	a := make([]string, len(m.Other)) | ||||
| 	for i, e := range m.Other { | ||||
| 		a[i] = e.String() | ||||
| 	} | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| type XMLElement struct { | ||||
| 	XMLName  xml.Name | ||||
| 	InnerXML string `xml:",innerxml"` | ||||
| } | ||||
| 
 | ||||
| func (e *XMLElement) String() string { | ||||
| 	r := bytes.NewReader([]byte(e.InnerXML)) | ||||
| 	d := xml.NewDecoder(r) | ||||
| 	var buf bytes.Buffer | ||||
| 	for { | ||||
| 		tok, err := d.Token() | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		switch v := tok.(type) { | ||||
| 		case xml.StartElement: | ||||
| 			err = d.Skip() | ||||
| 		case xml.CharData: | ||||
| 			_, err = buf.Write(v) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| type Delay struct { | ||||
| 	Stamp string `xml:"stamp,attr"` | ||||
| } | ||||
| 
 | ||||
| type clientText struct { | ||||
| 	Lang string `xml:",attr"` | ||||
| 	Body string `xml:"chardata"` | ||||
| } | ||||
| 
 | ||||
| type clientPresence struct { | ||||
| 	XMLName xml.Name `xml:"jabber:client presence"` | ||||
| 	From    string   `xml:"from,attr"` | ||||
| 	ID      string   `xml:"id,attr"` | ||||
| 	To      string   `xml:"to,attr"` | ||||
| 	Type    string   `xml:"type,attr"` // error, probe, subscribe, subscribed, unavailable, unsubscribe, unsubscribed
 | ||||
| 	Lang    string   `xml:"lang,attr"` | ||||
| 
 | ||||
| 	Show     string `xml:"show"`   // away, chat, dnd, xa
 | ||||
| 	Status   string `xml:"status"` // sb []clientText
 | ||||
| 	Priority string `xml:"priority,attr"` | ||||
| 	Error    *clientError | ||||
| } | ||||
| 
 | ||||
| type clientIQ struct { // info/query
 | ||||
| 	XMLName xml.Name `xml:"jabber:client iq"` | ||||
| 	From    string   `xml:"from,attr"` | ||||
| 	ID      string   `xml:"id,attr"` | ||||
| 	To      string   `xml:"to,attr"` | ||||
| 	Type    string   `xml:"type,attr"` // error, get, result, set
 | ||||
| 	Query   []byte   `xml:",innerxml"` | ||||
| 	Error   clientError | ||||
| 	Bind    bindBind | ||||
| } | ||||
| 
 | ||||
| type clientError struct { | ||||
| 	XMLName xml.Name `xml:"jabber:client error"` | ||||
| 	Code    string   `xml:",attr"` | ||||
| 	Type    string   `xml:",attr"` | ||||
| 	Any     xml.Name | ||||
| 	Text    string | ||||
| } | ||||
| 
 | ||||
| type clientQuery struct { | ||||
| 	Item []rosterItem | ||||
| } | ||||
| 
 | ||||
| type rosterItem struct { | ||||
| 	XMLName      xml.Name `xml:"jabber:iq:roster item"` | ||||
| 	Jid          string   `xml:",attr"` | ||||
| 	Name         string   `xml:",attr"` | ||||
| 	Subscription string   `xml:",attr"` | ||||
| 	Group        []string | ||||
| } | ||||
| 
 | ||||
| // Scan XML token stream to find next StartElement.
 | ||||
| func nextStart(p *xml.Decoder) (xml.StartElement, error) { | ||||
| 	for { | ||||
| 		t, err := p.Token() | ||||
| 		if err != nil && err != io.EOF || t == nil { | ||||
| 			return xml.StartElement{}, err | ||||
| 		} | ||||
| 		switch t := t.(type) { | ||||
| 		case xml.StartElement: | ||||
| 			return t, nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Scan XML token stream for next element and save into val.
 | ||||
| // If val == nil, allocate new element based on proto map.
 | ||||
| // Either way, return val.
 | ||||
| func next(p *xml.Decoder) (xml.Name, interface{}, error) { | ||||
| 	// Read start element to find out what type we want.
 | ||||
| 	se, err := nextStart(p) | ||||
| 	if err != nil { | ||||
| 		return xml.Name{}, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Put it in an interface and allocate one.
 | ||||
| 	var nv interface{} | ||||
| 	switch se.Name.Space + " " + se.Name.Local { | ||||
| 	case nsStream + " features": | ||||
| 		nv = &streamFeatures{} | ||||
| 	case nsStream + " error": | ||||
| 		nv = &streamError{} | ||||
| 	case nsTLS + " starttls": | ||||
| 		nv = &tlsStartTLS{} | ||||
| 	case nsTLS + " proceed": | ||||
| 		nv = &tlsProceed{} | ||||
| 	case nsTLS + " failure": | ||||
| 		nv = &tlsFailure{} | ||||
| 	case nsSASL + " mechanisms": | ||||
| 		nv = &saslMechanisms{} | ||||
| 	case nsSASL + " challenge": | ||||
| 		nv = "" | ||||
| 	case nsSASL + " response": | ||||
| 		nv = "" | ||||
| 	case nsSASL + " abort": | ||||
| 		nv = &saslAbort{} | ||||
| 	case nsSASL + " success": | ||||
| 		nv = &saslSuccess{} | ||||
| 	case nsSASL + " failure": | ||||
| 		nv = &saslFailure{} | ||||
| 	case nsBind + " bind": | ||||
| 		nv = &bindBind{} | ||||
| 	case nsClient + " message": | ||||
| 		nv = &clientMessage{} | ||||
| 	case nsClient + " presence": | ||||
| 		nv = &clientPresence{} | ||||
| 	case nsClient + " iq": | ||||
| 		nv = &clientIQ{} | ||||
| 	case nsClient + " error": | ||||
| 		nv = &clientError{} | ||||
| 	default: | ||||
| 		return xml.Name{}, nil, errors.New("unexpected XMPP message " + | ||||
| 			se.Name.Space + " <" + se.Name.Local + "/>") | ||||
| 	} | ||||
| 
 | ||||
| 	// Unmarshal into that storage.
 | ||||
| 	if err = p.DecodeElement(nv, &se); err != nil { | ||||
| 		return xml.Name{}, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return se.Name, nv, err | ||||
| } | ||||
| 
 | ||||
| var xmlSpecial = map[byte]string{ | ||||
| 	'<':  "<", | ||||
| 	'>':  ">", | ||||
| 	'"':  """, | ||||
| 	'\'': "'", | ||||
| 	'&':  "&", | ||||
| } | ||||
| 
 | ||||
| func xmlEscape(s string) string { | ||||
| 	var b bytes.Buffer | ||||
| 	for i := 0; i < len(s); i++ { | ||||
| 		c := s[i] | ||||
| 		if s, ok := xmlSpecial[c]; ok { | ||||
| 			b.WriteString(s) | ||||
| 		} else { | ||||
| 			b.WriteByte(c) | ||||
| 		} | ||||
| 	} | ||||
| 	return b.String() | ||||
| } | ||||
| 
 | ||||
| type tee struct { | ||||
| 	r io.Reader | ||||
| 	w io.Writer | ||||
| } | ||||
| 
 | ||||
| func (t tee) Read(p []byte) (n int, err error) { | ||||
| 	n, err = t.r.Read(p) | ||||
| 	if n > 0 { | ||||
| 		t.w.Write(p[0:n]) | ||||
| 		t.w.Write([]byte("\n")) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | @ -1,24 +0,0 @@ | |||
| package xmpp | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| const IQTypeGet = "get" | ||||
| const IQTypeSet = "set" | ||||
| const IQTypeResult = "result" | ||||
| 
 | ||||
| func (c *Client) Discovery() (string, error) { | ||||
| 	const namespace = "http://jabber.org/protocol/disco#items" | ||||
| 	// use getCookie for a pseudo random id.
 | ||||
| 	reqID := strconv.FormatUint(uint64(getCookie()), 10) | ||||
| 	return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "") | ||||
| } | ||||
| 
 | ||||
| // RawInformationQuery sends an information query request to the server.
 | ||||
| func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) { | ||||
| 	const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>" | ||||
| 	_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body) | ||||
| 	return id, err | ||||
| } | ||||
|  | @ -1,134 +0,0 @@ | |||
| // Copyright 2013 Flo Lauber <dev@qatfy.at>.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // TODO(flo):
 | ||||
| //   - support password protected MUC rooms
 | ||||
| //   - cleanup signatures of join/leave functions
 | ||||
| package xmpp | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 	"errors" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	nsMUC     = "http://jabber.org/protocol/muc" | ||||
| 	nsMUCUser = "http://jabber.org/protocol/muc#user" | ||||
| 	NoHistory = 0 | ||||
| 	CharHistory = 1 | ||||
| 	StanzaHistory = 2 | ||||
| 	SecondsHistory = 3 | ||||
| 	SinceHistory = 4 | ||||
| ) | ||||
| 
 | ||||
| // Send sends room topic wrapped inside an XMPP message stanza body.
 | ||||
| func (c *Client) SendTopic(chat Chat) (n int, err error) { | ||||
| 	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>", | ||||
| 		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text)) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) { | ||||
| 	if nick == "" { | ||||
| 		nick = c.jid | ||||
| 	} | ||||
| 	return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+ | ||||
| 		"<x xmlns='%s'>"+ | ||||
| 		"<history maxchars='0'/></x>\n"+ | ||||
| 		"</presence>", | ||||
| 		xmlEscape(jid), xmlEscape(nick), nsMUC) | ||||
| } | ||||
| 
 | ||||
| // xep-0045 7.2
 | ||||
| func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_date *time.Time) (n int, err error) { | ||||
| 	if nick == "" { | ||||
| 		nick = c.jid | ||||
| 	} | ||||
| 	switch history_type { | ||||
| 	case NoHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s' />\n" + | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC) | ||||
| 	case CharHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<history maxchars='%d'/></x>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, history) | ||||
| 	case StanzaHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<history maxstanzas='%d'/></x>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, history) | ||||
| 	case SecondsHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<history seconds='%d'/></x>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, history) | ||||
| 	case SinceHistory: | ||||
| 		if history_date != nil { | ||||
| 			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 				"<x xmlns='%s'>\n" + | ||||
| 				"<history since='%s'/></x>\n" + | ||||
| 				"</presence>", | ||||
| 					xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339)) | ||||
| 		} | ||||
| 	} | ||||
| 	return 0, errors.New("Unknown history option") | ||||
| } | ||||
| 
 | ||||
| // xep-0045 7.2.6
 | ||||
| func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_type, history int, history_date *time.Time) (n int, err error) { | ||||
| 	if nick == "" { | ||||
| 		nick = c.jid | ||||
| 	} | ||||
| 	switch history_type { | ||||
| 	case NoHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<password>%s</password>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password)) | ||||
| 	case CharHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<password>%s</password>\n"+ | ||||
| 			"<history maxchars='%d'/></x>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history) | ||||
| 	case StanzaHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<password>%s</password>\n"+ | ||||
| 			"<history maxstanzas='%d'/></x>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history) | ||||
| 	case SecondsHistory: | ||||
| 		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 			"<x xmlns='%s'>\n" + | ||||
| 			"<password>%s</password>\n"+ | ||||
| 			"<history seconds='%d'/></x>\n"+ | ||||
| 			"</presence>", | ||||
| 				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history) | ||||
| 	case SinceHistory: | ||||
| 		if history_date != nil { | ||||
| 			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" + | ||||
| 				"<x xmlns='%s'>\n" + | ||||
| 				"<password>%s</password>\n"+ | ||||
| 				"<history since='%s'/></x>\n" + | ||||
| 				"</presence>", | ||||
| 					xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339)) | ||||
| 		} | ||||
| 	} | ||||
| 	return 0, errors.New("Unknown history option") | ||||
| } | ||||
| 
 | ||||
| // xep-0045 7.14
 | ||||
| func (c *Client) LeaveMUC(jid string) (n int, err error) { | ||||
| 	return fmt.Fprintf(c.conn, "<presence from='%s' to='%s' type='unavailable' />", | ||||
| 		c.jid, xmlEscape(jid)) | ||||
| } | ||||
|  | @ -1,33 +0,0 @@ | |||
| package xmpp | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| func (c *Client) PingC2S(jid, server string) error { | ||||
| 	if jid == "" { | ||||
| 		jid = c.jid | ||||
| 	} | ||||
| 	if server == "" { | ||||
| 		server = c.domain | ||||
| 	} | ||||
| 	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+ | ||||
| 		"<ping xmlns='urn:xmpp:ping'/>\n"+ | ||||
| 		"</iq>", | ||||
| 		xmlEscape(jid), xmlEscape(server)) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (c *Client) PingS2S(fromServer, toServer string) error { | ||||
| 	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+ | ||||
| 		"<ping xmlns='urn:xmpp:ping'/>\n"+ | ||||
| 		"</iq>", | ||||
| 		xmlEscape(fromServer), xmlEscape(toServer)) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (c *Client) SendResultPing(id, toServer string) error { | ||||
| 	_, err := fmt.Fprintf(c.conn, "<iq type='result' to='%s' id='%s'/>", | ||||
| 		xmlEscape(toServer), xmlEscape(id)) | ||||
| 	return err | ||||
| } | ||||
|  | @ -1,20 +0,0 @@ | |||
| package xmpp | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| func (c *Client) ApproveSubscription(jid string) { | ||||
| 	fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>", | ||||
| 		xmlEscape(jid)) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) RevokeSubscription(jid string) { | ||||
| 	fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>", | ||||
| 		xmlEscape(jid)) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) RequestSubscription(jid string) { | ||||
| 	fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>", | ||||
| 		xmlEscape(jid)) | ||||
| } | ||||
|  | @ -1,10 +0,0 @@ | |||
| # How to contribute | ||||
| 
 | ||||
| We definitely welcome patches and contribution to this project! | ||||
| 
 | ||||
| ### Legal requirements | ||||
| 
 | ||||
| In order to protect both you and ourselves, you will need to sign the | ||||
| [Contributor License Agreement](https://cla.developers.google.com/clas). | ||||
| 
 | ||||
| You may have already signed it for other Google projects. | ||||
|  | @ -1 +0,0 @@ | |||
| Paul Borman <borman@google.com> | ||||
|  | @ -1,27 +0,0 @@ | |||
| Copyright (c) 2009,2014 Google Inc. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | @ -1,13 +0,0 @@ | |||
| This project was automatically exported from code.google.com/p/go-uuid | ||||
| 
 | ||||
| # uuid  | ||||
| The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services.  | ||||
| 
 | ||||
| ###### Install | ||||
| `go get github.com/pborman/uuid` | ||||
| 
 | ||||
| ###### Documentation  | ||||
| [](http://godoc.org/github.com/pborman/uuid) | ||||
| 
 | ||||
| Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:  | ||||
| http://godoc.org/github.com/pborman/uuid | ||||
|  | @ -1,84 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| // A Domain represents a Version 2 domain
 | ||||
| type Domain byte | ||||
| 
 | ||||
| // Domain constants for DCE Security (Version 2) UUIDs.
 | ||||
| const ( | ||||
| 	Person = Domain(0) | ||||
| 	Group  = Domain(1) | ||||
| 	Org    = Domain(2) | ||||
| ) | ||||
| 
 | ||||
| // NewDCESecurity returns a DCE Security (Version 2) UUID.
 | ||||
| //
 | ||||
| // The domain should be one of Person, Group or Org.
 | ||||
| // On a POSIX system the id should be the users UID for the Person
 | ||||
| // domain and the users GID for the Group.  The meaning of id for
 | ||||
| // the domain Org or on non-POSIX systems is site defined.
 | ||||
| //
 | ||||
| // For a given domain/id pair the same token may be returned for up to
 | ||||
| // 7 minutes and 10 seconds.
 | ||||
| func NewDCESecurity(domain Domain, id uint32) UUID { | ||||
| 	uuid := NewUUID() | ||||
| 	if uuid != nil { | ||||
| 		uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
 | ||||
| 		uuid[9] = byte(domain) | ||||
| 		binary.BigEndian.PutUint32(uuid[0:], id) | ||||
| 	} | ||||
| 	return uuid | ||||
| } | ||||
| 
 | ||||
| // NewDCEPerson returns a DCE Security (Version 2) UUID in the person
 | ||||
| // domain with the id returned by os.Getuid.
 | ||||
| //
 | ||||
| //  NewDCEPerson(Person, uint32(os.Getuid()))
 | ||||
| func NewDCEPerson() UUID { | ||||
| 	return NewDCESecurity(Person, uint32(os.Getuid())) | ||||
| } | ||||
| 
 | ||||
| // NewDCEGroup returns a DCE Security (Version 2) UUID in the group
 | ||||
| // domain with the id returned by os.Getgid.
 | ||||
| //
 | ||||
| //  NewDCEGroup(Group, uint32(os.Getgid()))
 | ||||
| func NewDCEGroup() UUID { | ||||
| 	return NewDCESecurity(Group, uint32(os.Getgid())) | ||||
| } | ||||
| 
 | ||||
| // Domain returns the domain for a Version 2 UUID or false.
 | ||||
| func (uuid UUID) Domain() (Domain, bool) { | ||||
| 	if v, _ := uuid.Version(); v != 2 { | ||||
| 		return 0, false | ||||
| 	} | ||||
| 	return Domain(uuid[9]), true | ||||
| } | ||||
| 
 | ||||
| // Id returns the id for a Version 2 UUID or false.
 | ||||
| func (uuid UUID) Id() (uint32, bool) { | ||||
| 	if v, _ := uuid.Version(); v != 2 { | ||||
| 		return 0, false | ||||
| 	} | ||||
| 	return binary.BigEndian.Uint32(uuid[0:4]), true | ||||
| } | ||||
| 
 | ||||
| func (d Domain) String() string { | ||||
| 	switch d { | ||||
| 	case Person: | ||||
| 		return "Person" | ||||
| 	case Group: | ||||
| 		return "Group" | ||||
| 	case Org: | ||||
| 		return "Org" | ||||
| 	} | ||||
| 	return fmt.Sprintf("Domain%d", int(d)) | ||||
| } | ||||
|  | @ -1,8 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // The uuid package generates and inspects UUIDs.
 | ||||
| //
 | ||||
| // UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
 | ||||
| package uuid | ||||
|  | @ -1,53 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/md5" | ||||
| 	"crypto/sha1" | ||||
| 	"hash" | ||||
| ) | ||||
| 
 | ||||
| // Well known Name Space IDs and UUIDs
 | ||||
| var ( | ||||
| 	NameSpace_DNS  = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") | ||||
| 	NameSpace_URL  = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") | ||||
| 	NameSpace_OID  = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") | ||||
| 	NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") | ||||
| 	NIL            = Parse("00000000-0000-0000-0000-000000000000") | ||||
| ) | ||||
| 
 | ||||
| // NewHash returns a new UUID derived from the hash of space concatenated with
 | ||||
| // data generated by h.  The hash should be at least 16 byte in length.  The
 | ||||
| // first 16 bytes of the hash are used to form the UUID.  The version of the
 | ||||
| // UUID will be the lower 4 bits of version.  NewHash is used to implement
 | ||||
| // NewMD5 and NewSHA1.
 | ||||
| func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { | ||||
| 	h.Reset() | ||||
| 	h.Write(space) | ||||
| 	h.Write([]byte(data)) | ||||
| 	s := h.Sum(nil) | ||||
| 	uuid := make([]byte, 16) | ||||
| 	copy(uuid, s) | ||||
| 	uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) | ||||
| 	uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
 | ||||
| 	return uuid | ||||
| } | ||||
| 
 | ||||
| // NewMD5 returns a new MD5 (Version 3) UUID based on the
 | ||||
| // supplied name space and data.
 | ||||
| //
 | ||||
| //  NewHash(md5.New(), space, data, 3)
 | ||||
| func NewMD5(space UUID, data []byte) UUID { | ||||
| 	return NewHash(md5.New(), space, data, 3) | ||||
| } | ||||
| 
 | ||||
| // NewSHA1 returns a new SHA1 (Version 5) UUID based on the
 | ||||
| // supplied name space and data.
 | ||||
| //
 | ||||
| //  NewHash(sha1.New(), space, data, 5)
 | ||||
| func NewSHA1(space UUID, data []byte) UUID { | ||||
| 	return NewHash(sha1.New(), space, data, 5) | ||||
| } | ||||
|  | @ -1,83 +0,0 @@ | |||
| // Copyright 2016 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| // MarshalText implements encoding.TextMarshaler.
 | ||||
| func (u UUID) MarshalText() ([]byte, error) { | ||||
| 	if len(u) != 16 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	var js [36]byte | ||||
| 	encodeHex(js[:], u) | ||||
| 	return js[:], nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalText implements encoding.TextUnmarshaler.
 | ||||
| func (u *UUID) UnmarshalText(data []byte) error { | ||||
| 	if len(data) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	id := Parse(string(data)) | ||||
| 	if id == nil { | ||||
| 		return errors.New("invalid UUID") | ||||
| 	} | ||||
| 	*u = id | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // MarshalBinary implements encoding.BinaryMarshaler.
 | ||||
| func (u UUID) MarshalBinary() ([]byte, error) { | ||||
| 	return u[:], nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalBinary implements encoding.BinaryUnmarshaler.
 | ||||
| func (u *UUID) UnmarshalBinary(data []byte) error { | ||||
| 	if len(data) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if len(data) != 16 { | ||||
| 		return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) | ||||
| 	} | ||||
| 	var id [16]byte | ||||
| 	copy(id[:], data) | ||||
| 	*u = id[:] | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // MarshalText implements encoding.TextMarshaler.
 | ||||
| func (u Array) MarshalText() ([]byte, error) { | ||||
| 	var js [36]byte | ||||
| 	encodeHex(js[:], u[:]) | ||||
| 	return js[:], nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalText implements encoding.TextUnmarshaler.
 | ||||
| func (u *Array) UnmarshalText(data []byte) error { | ||||
| 	id := Parse(string(data)) | ||||
| 	if id == nil { | ||||
| 		return errors.New("invalid UUID") | ||||
| 	} | ||||
| 	*u = id.Array() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // MarshalBinary implements encoding.BinaryMarshaler.
 | ||||
| func (u Array) MarshalBinary() ([]byte, error) { | ||||
| 	return u[:], nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalBinary implements encoding.BinaryUnmarshaler.
 | ||||
| func (u *Array) UnmarshalBinary(data []byte) error { | ||||
| 	if len(data) != 16 { | ||||
| 		return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) | ||||
| 	} | ||||
| 	copy(u[:], data) | ||||
| 	return nil | ||||
| } | ||||
|  | @ -1,117 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	nodeMu     sync.Mutex | ||||
| 	interfaces []net.Interface // cached list of interfaces
 | ||||
| 	ifname     string          // name of interface being used
 | ||||
| 	nodeID     []byte          // hardware for version 1 UUIDs
 | ||||
| ) | ||||
| 
 | ||||
| // NodeInterface returns the name of the interface from which the NodeID was
 | ||||
| // derived.  The interface "user" is returned if the NodeID was set by
 | ||||
| // SetNodeID.
 | ||||
| func NodeInterface() string { | ||||
| 	defer nodeMu.Unlock() | ||||
| 	nodeMu.Lock() | ||||
| 	return ifname | ||||
| } | ||||
| 
 | ||||
| // SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
 | ||||
| // If name is "" then the first usable interface found will be used or a random
 | ||||
| // Node ID will be generated.  If a named interface cannot be found then false
 | ||||
| // is returned.
 | ||||
| //
 | ||||
| // SetNodeInterface never fails when name is "".
 | ||||
| func SetNodeInterface(name string) bool { | ||||
| 	defer nodeMu.Unlock() | ||||
| 	nodeMu.Lock() | ||||
| 	return setNodeInterface(name) | ||||
| } | ||||
| 
 | ||||
| func setNodeInterface(name string) bool { | ||||
| 	if interfaces == nil { | ||||
| 		var err error | ||||
| 		interfaces, err = net.Interfaces() | ||||
| 		if err != nil && name != "" { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for _, ifs := range interfaces { | ||||
| 		if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { | ||||
| 			if setNodeID(ifs.HardwareAddr) { | ||||
| 				ifname = ifs.Name | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// We found no interfaces with a valid hardware address.  If name
 | ||||
| 	// does not specify a specific interface generate a random Node ID
 | ||||
| 	// (section 4.1.6)
 | ||||
| 	if name == "" { | ||||
| 		if nodeID == nil { | ||||
| 			nodeID = make([]byte, 6) | ||||
| 		} | ||||
| 		randomBits(nodeID) | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // NodeID returns a slice of a copy of the current Node ID, setting the Node ID
 | ||||
| // if not already set.
 | ||||
| func NodeID() []byte { | ||||
| 	defer nodeMu.Unlock() | ||||
| 	nodeMu.Lock() | ||||
| 	if nodeID == nil { | ||||
| 		setNodeInterface("") | ||||
| 	} | ||||
| 	nid := make([]byte, 6) | ||||
| 	copy(nid, nodeID) | ||||
| 	return nid | ||||
| } | ||||
| 
 | ||||
| // SetNodeID sets the Node ID to be used for Version 1 UUIDs.  The first 6 bytes
 | ||||
| // of id are used.  If id is less than 6 bytes then false is returned and the
 | ||||
| // Node ID is not set.
 | ||||
| func SetNodeID(id []byte) bool { | ||||
| 	defer nodeMu.Unlock() | ||||
| 	nodeMu.Lock() | ||||
| 	if setNodeID(id) { | ||||
| 		ifname = "user" | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func setNodeID(id []byte) bool { | ||||
| 	if len(id) < 6 { | ||||
| 		return false | ||||
| 	} | ||||
| 	if nodeID == nil { | ||||
| 		nodeID = make([]byte, 6) | ||||
| 	} | ||||
| 	copy(nodeID, id) | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // NodeID returns the 6 byte node id encoded in uuid.  It returns nil if uuid is
 | ||||
| // not valid.  The NodeID is only well defined for version 1 and 2 UUIDs.
 | ||||
| func (uuid UUID) NodeID() []byte { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	node := make([]byte, 6) | ||||
| 	copy(node, uuid[10:]) | ||||
| 	return node | ||||
| } | ||||
|  | @ -1,66 +0,0 @@ | |||
| // Copyright 2015 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| // Scan implements sql.Scanner so UUIDs can be read from databases transparently
 | ||||
| // Currently, database types that map to string and []byte are supported. Please
 | ||||
| // consult database-specific driver documentation for matching types.
 | ||||
| func (uuid *UUID) Scan(src interface{}) error { | ||||
| 	switch src.(type) { | ||||
| 	case string: | ||||
| 		// if an empty UUID comes from a table, we return a null UUID
 | ||||
| 		if src.(string) == "" { | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		// see uuid.Parse for required string format
 | ||||
| 		parsed := Parse(src.(string)) | ||||
| 
 | ||||
| 		if parsed == nil { | ||||
| 			return errors.New("Scan: invalid UUID format") | ||||
| 		} | ||||
| 
 | ||||
| 		*uuid = parsed | ||||
| 	case []byte: | ||||
| 		b := src.([]byte) | ||||
| 
 | ||||
| 		// if an empty UUID comes from a table, we return a null UUID
 | ||||
| 		if len(b) == 0 { | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		// assumes a simple slice of bytes if 16 bytes
 | ||||
| 		// otherwise attempts to parse
 | ||||
| 		if len(b) == 16 { | ||||
| 			*uuid = UUID(b) | ||||
| 		} else { | ||||
| 			u := Parse(string(b)) | ||||
| 
 | ||||
| 			if u == nil { | ||||
| 				return errors.New("Scan: invalid UUID format") | ||||
| 			} | ||||
| 
 | ||||
| 			*uuid = u | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		return fmt.Errorf("Scan: unable to scan type %T into UUID", src) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Value implements sql.Valuer so that UUIDs can be written to databases
 | ||||
| // transparently. Currently, UUIDs map to strings. Please consult
 | ||||
| // database-specific driver documentation for matching types.
 | ||||
| func (uuid UUID) Value() (driver.Value, error) { | ||||
| 	return uuid.String(), nil | ||||
| } | ||||
|  | @ -1,132 +0,0 @@ | |||
| // Copyright 2014 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // A Time represents a time as the number of 100's of nanoseconds since 15 Oct
 | ||||
| // 1582.
 | ||||
| type Time int64 | ||||
| 
 | ||||
| const ( | ||||
| 	lillian    = 2299160          // Julian day of 15 Oct 1582
 | ||||
| 	unix       = 2440587          // Julian day of 1 Jan 1970
 | ||||
| 	epoch      = unix - lillian   // Days between epochs
 | ||||
| 	g1582      = epoch * 86400    // seconds between epochs
 | ||||
| 	g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
 | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	timeMu    sync.Mutex | ||||
| 	lasttime  uint64 // last time we returned
 | ||||
| 	clock_seq uint16 // clock sequence for this run
 | ||||
| 
 | ||||
| 	timeNow = time.Now // for testing
 | ||||
| ) | ||||
| 
 | ||||
| // UnixTime converts t the number of seconds and nanoseconds using the Unix
 | ||||
| // epoch of 1 Jan 1970.
 | ||||
| func (t Time) UnixTime() (sec, nsec int64) { | ||||
| 	sec = int64(t - g1582ns100) | ||||
| 	nsec = (sec % 10000000) * 100 | ||||
| 	sec /= 10000000 | ||||
| 	return sec, nsec | ||||
| } | ||||
| 
 | ||||
| // GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
 | ||||
| // clock sequence as well as adjusting the clock sequence as needed.  An error
 | ||||
| // is returned if the current time cannot be determined.
 | ||||
| func GetTime() (Time, uint16, error) { | ||||
| 	defer timeMu.Unlock() | ||||
| 	timeMu.Lock() | ||||
| 	return getTime() | ||||
| } | ||||
| 
 | ||||
| func getTime() (Time, uint16, error) { | ||||
| 	t := timeNow() | ||||
| 
 | ||||
| 	// If we don't have a clock sequence already, set one.
 | ||||
| 	if clock_seq == 0 { | ||||
| 		setClockSequence(-1) | ||||
| 	} | ||||
| 	now := uint64(t.UnixNano()/100) + g1582ns100 | ||||
| 
 | ||||
| 	// If time has gone backwards with this clock sequence then we
 | ||||
| 	// increment the clock sequence
 | ||||
| 	if now <= lasttime { | ||||
| 		clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000 | ||||
| 	} | ||||
| 	lasttime = now | ||||
| 	return Time(now), clock_seq, nil | ||||
| } | ||||
| 
 | ||||
| // ClockSequence returns the current clock sequence, generating one if not
 | ||||
| // already set.  The clock sequence is only used for Version 1 UUIDs.
 | ||||
| //
 | ||||
| // The uuid package does not use global static storage for the clock sequence or
 | ||||
| // the last time a UUID was generated.  Unless SetClockSequence a new random
 | ||||
| // clock sequence is generated the first time a clock sequence is requested by
 | ||||
| // ClockSequence, GetTime, or NewUUID.  (section 4.2.1.1) sequence is generated
 | ||||
| // for
 | ||||
| func ClockSequence() int { | ||||
| 	defer timeMu.Unlock() | ||||
| 	timeMu.Lock() | ||||
| 	return clockSequence() | ||||
| } | ||||
| 
 | ||||
| func clockSequence() int { | ||||
| 	if clock_seq == 0 { | ||||
| 		setClockSequence(-1) | ||||
| 	} | ||||
| 	return int(clock_seq & 0x3fff) | ||||
| } | ||||
| 
 | ||||
| // SetClockSeq sets the clock sequence to the lower 14 bits of seq.  Setting to
 | ||||
| // -1 causes a new sequence to be generated.
 | ||||
| func SetClockSequence(seq int) { | ||||
| 	defer timeMu.Unlock() | ||||
| 	timeMu.Lock() | ||||
| 	setClockSequence(seq) | ||||
| } | ||||
| 
 | ||||
| func setClockSequence(seq int) { | ||||
| 	if seq == -1 { | ||||
| 		var b [2]byte | ||||
| 		randomBits(b[:]) // clock sequence
 | ||||
| 		seq = int(b[0])<<8 | int(b[1]) | ||||
| 	} | ||||
| 	old_seq := clock_seq | ||||
| 	clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
 | ||||
| 	if old_seq != clock_seq { | ||||
| 		lasttime = 0 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
 | ||||
| // uuid.  It returns false if uuid is not valid.  The time is only well defined
 | ||||
| // for version 1 and 2 UUIDs.
 | ||||
| func (uuid UUID) Time() (Time, bool) { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return 0, false | ||||
| 	} | ||||
| 	time := int64(binary.BigEndian.Uint32(uuid[0:4])) | ||||
| 	time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 | ||||
| 	time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 | ||||
| 	return Time(time), true | ||||
| } | ||||
| 
 | ||||
| // ClockSequence returns the clock sequence encoded in uuid.  It returns false
 | ||||
| // if uuid is not valid.  The clock sequence is only well defined for version 1
 | ||||
| // and 2 UUIDs.
 | ||||
| func (uuid UUID) ClockSequence() (int, bool) { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return 0, false | ||||
| 	} | ||||
| 	return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true | ||||
| } | ||||
|  | @ -1,43 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| // randomBits completely fills slice b with random data.
 | ||||
| func randomBits(b []byte) { | ||||
| 	if _, err := io.ReadFull(rander, b); err != nil { | ||||
| 		panic(err.Error()) // rand should never fail
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // xvalues returns the value of a byte as a hexadecimal digit or 255.
 | ||||
| var xvalues = [256]byte{ | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||||
| } | ||||
| 
 | ||||
| // xtob converts the the first two hex bytes of x into a byte.
 | ||||
| func xtob(x string) (byte, bool) { | ||||
| 	b1 := xvalues[x[0]] | ||||
| 	b2 := xvalues[x[1]] | ||||
| 	return (b1 << 4) | b2, b1 != 255 && b2 != 255 | ||||
| } | ||||
|  | @ -1,201 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Array is a pass-by-value UUID that can be used as an effecient key in a map.
 | ||||
| type Array [16]byte | ||||
| 
 | ||||
| // UUID converts uuid into a slice.
 | ||||
| func (uuid Array) UUID() UUID { | ||||
| 	return uuid[:] | ||||
| } | ||||
| 
 | ||||
| // String returns the string representation of uuid,
 | ||||
| // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
 | ||||
| func (uuid Array) String() string { | ||||
| 	return uuid.UUID().String() | ||||
| } | ||||
| 
 | ||||
| // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
 | ||||
| // 4122.
 | ||||
| type UUID []byte | ||||
| 
 | ||||
| // A Version represents a UUIDs version.
 | ||||
| type Version byte | ||||
| 
 | ||||
| // A Variant represents a UUIDs variant.
 | ||||
| type Variant byte | ||||
| 
 | ||||
| // Constants returned by Variant.
 | ||||
| const ( | ||||
| 	Invalid   = Variant(iota) // Invalid UUID
 | ||||
| 	RFC4122                   // The variant specified in RFC4122
 | ||||
| 	Reserved                  // Reserved, NCS backward compatibility.
 | ||||
| 	Microsoft                 // Reserved, Microsoft Corporation backward compatibility.
 | ||||
| 	Future                    // Reserved for future definition.
 | ||||
| ) | ||||
| 
 | ||||
| var rander = rand.Reader // random function
 | ||||
| 
 | ||||
| // New returns a new random (version 4) UUID as a string.  It is a convenience
 | ||||
| // function for NewRandom().String().
 | ||||
| func New() string { | ||||
| 	return NewRandom().String() | ||||
| } | ||||
| 
 | ||||
| // Parse decodes s into a UUID or returns nil.  Both the UUID form of
 | ||||
| // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
 | ||||
| // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
 | ||||
| func Parse(s string) UUID { | ||||
| 	if len(s) == 36+9 { | ||||
| 		if strings.ToLower(s[:9]) != "urn:uuid:" { | ||||
| 			return nil | ||||
| 		} | ||||
| 		s = s[9:] | ||||
| 	} else if len(s) != 36 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { | ||||
| 		return nil | ||||
| 	} | ||||
| 	var uuid [16]byte | ||||
| 	for i, x := range [16]int{ | ||||
| 		0, 2, 4, 6, | ||||
| 		9, 11, | ||||
| 		14, 16, | ||||
| 		19, 21, | ||||
| 		24, 26, 28, 30, 32, 34} { | ||||
| 		if v, ok := xtob(s[x:]); !ok { | ||||
| 			return nil | ||||
| 		} else { | ||||
| 			uuid[i] = v | ||||
| 		} | ||||
| 	} | ||||
| 	return uuid[:] | ||||
| } | ||||
| 
 | ||||
| // Equal returns true if uuid1 and uuid2 are equal.
 | ||||
| func Equal(uuid1, uuid2 UUID) bool { | ||||
| 	return bytes.Equal(uuid1, uuid2) | ||||
| } | ||||
| 
 | ||||
| // Array returns an array representation of uuid that can be used as a map key.
 | ||||
| // Array panics if uuid is not valid.
 | ||||
| func (uuid UUID) Array() Array { | ||||
| 	if len(uuid) != 16 { | ||||
| 		panic("invalid uuid") | ||||
| 	} | ||||
| 	var a Array | ||||
| 	copy(a[:], uuid) | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
 | ||||
| // , or "" if uuid is invalid.
 | ||||
| func (uuid UUID) String() string { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	var buf [36]byte | ||||
| 	encodeHex(buf[:], uuid) | ||||
| 	return string(buf[:]) | ||||
| } | ||||
| 
 | ||||
| // URN returns the RFC 2141 URN form of uuid,
 | ||||
| // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,  or "" if uuid is invalid.
 | ||||
| func (uuid UUID) URN() string { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	var buf [36 + 9]byte | ||||
| 	copy(buf[:], "urn:uuid:") | ||||
| 	encodeHex(buf[9:], uuid) | ||||
| 	return string(buf[:]) | ||||
| } | ||||
| 
 | ||||
| func encodeHex(dst []byte, uuid UUID) { | ||||
| 	hex.Encode(dst[:], uuid[:4]) | ||||
| 	dst[8] = '-' | ||||
| 	hex.Encode(dst[9:13], uuid[4:6]) | ||||
| 	dst[13] = '-' | ||||
| 	hex.Encode(dst[14:18], uuid[6:8]) | ||||
| 	dst[18] = '-' | ||||
| 	hex.Encode(dst[19:23], uuid[8:10]) | ||||
| 	dst[23] = '-' | ||||
| 	hex.Encode(dst[24:], uuid[10:]) | ||||
| } | ||||
| 
 | ||||
| // Variant returns the variant encoded in uuid.  It returns Invalid if
 | ||||
| // uuid is invalid.
 | ||||
| func (uuid UUID) Variant() Variant { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return Invalid | ||||
| 	} | ||||
| 	switch { | ||||
| 	case (uuid[8] & 0xc0) == 0x80: | ||||
| 		return RFC4122 | ||||
| 	case (uuid[8] & 0xe0) == 0xc0: | ||||
| 		return Microsoft | ||||
| 	case (uuid[8] & 0xe0) == 0xe0: | ||||
| 		return Future | ||||
| 	default: | ||||
| 		return Reserved | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Version returns the version of uuid.  It returns false if uuid is not
 | ||||
| // valid.
 | ||||
| func (uuid UUID) Version() (Version, bool) { | ||||
| 	if len(uuid) != 16 { | ||||
| 		return 0, false | ||||
| 	} | ||||
| 	return Version(uuid[6] >> 4), true | ||||
| } | ||||
| 
 | ||||
| func (v Version) String() string { | ||||
| 	if v > 15 { | ||||
| 		return fmt.Sprintf("BAD_VERSION_%d", v) | ||||
| 	} | ||||
| 	return fmt.Sprintf("VERSION_%d", v) | ||||
| } | ||||
| 
 | ||||
| func (v Variant) String() string { | ||||
| 	switch v { | ||||
| 	case RFC4122: | ||||
| 		return "RFC4122" | ||||
| 	case Reserved: | ||||
| 		return "Reserved" | ||||
| 	case Microsoft: | ||||
| 		return "Microsoft" | ||||
| 	case Future: | ||||
| 		return "Future" | ||||
| 	case Invalid: | ||||
| 		return "Invalid" | ||||
| 	} | ||||
| 	return fmt.Sprintf("BadVariant%d", int(v)) | ||||
| } | ||||
| 
 | ||||
| // SetRand sets the random number generator to r, which implements io.Reader.
 | ||||
| // If r.Read returns an error when the package requests random data then
 | ||||
| // a panic will be issued.
 | ||||
| //
 | ||||
| // Calling SetRand with nil sets the random number generator to the default
 | ||||
| // generator.
 | ||||
| func SetRand(r io.Reader) { | ||||
| 	if r == nil { | ||||
| 		rander = rand.Reader | ||||
| 		return | ||||
| 	} | ||||
| 	rander = r | ||||
| } | ||||
|  | @ -1,41 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| ) | ||||
| 
 | ||||
| // NewUUID returns a Version 1 UUID based on the current NodeID and clock
 | ||||
| // sequence, and the current time.  If the NodeID has not been set by SetNodeID
 | ||||
| // or SetNodeInterface then it will be set automatically.  If the NodeID cannot
 | ||||
| // be set NewUUID returns nil.  If clock sequence has not been set by
 | ||||
| // SetClockSequence then it will be set automatically.  If GetTime fails to
 | ||||
| // return the current NewUUID returns nil.
 | ||||
| func NewUUID() UUID { | ||||
| 	if nodeID == nil { | ||||
| 		SetNodeInterface("") | ||||
| 	} | ||||
| 
 | ||||
| 	now, seq, err := GetTime() | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	uuid := make([]byte, 16) | ||||
| 
 | ||||
| 	time_low := uint32(now & 0xffffffff) | ||||
| 	time_mid := uint16((now >> 32) & 0xffff) | ||||
| 	time_hi := uint16((now >> 48) & 0x0fff) | ||||
| 	time_hi |= 0x1000 // Version 1
 | ||||
| 
 | ||||
| 	binary.BigEndian.PutUint32(uuid[0:], time_low) | ||||
| 	binary.BigEndian.PutUint16(uuid[4:], time_mid) | ||||
| 	binary.BigEndian.PutUint16(uuid[6:], time_hi) | ||||
| 	binary.BigEndian.PutUint16(uuid[8:], seq) | ||||
| 	copy(uuid[10:], nodeID) | ||||
| 
 | ||||
| 	return uuid | ||||
| } | ||||
|  | @ -1,25 +0,0 @@ | |||
| // Copyright 2011 Google Inc.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package uuid | ||||
| 
 | ||||
| // Random returns a Random (Version 4) UUID or panics.
 | ||||
| //
 | ||||
| // The strength of the UUIDs is based on the strength of the crypto/rand
 | ||||
| // package.
 | ||||
| //
 | ||||
| // A note about uniqueness derived from from the UUID Wikipedia entry:
 | ||||
| //
 | ||||
| //  Randomly generated UUIDs have 122 random bits.  One's annual risk of being
 | ||||
| //  hit by a meteorite is estimated to be one chance in 17 billion, that
 | ||||
| //  means the probability is about 0.00000000006 (6 × 10−11),
 | ||||
| //  equivalent to the odds of creating a few tens of trillions of UUIDs in a
 | ||||
| //  year and having one duplicate.
 | ||||
| func NewRandom() UUID { | ||||
| 	uuid := make([]byte, 16) | ||||
| 	randomBits([]byte(uuid)) | ||||
| 	uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
 | ||||
| 	uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
 | ||||
| 	return uuid | ||||
| } | ||||
|  | @ -8,6 +8,12 @@ | |||
| 			"revision": "7f4b1adc791766938c29457bed0703fb9134421a", | ||||
| 			"revisionTime": "2017-02-15T16:43:24Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "DuVew6znkXuUG1wjGQF+pUyXrHU=", | ||||
| 			"path": "github.com/appleboy/go-fcm", | ||||
| 			"revision": "5f2cb2866531e4e37c7a9ff5fb7a1536e1ffc566", | ||||
| 			"revisionTime": "2017-06-01T07:42:50Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "Ab7MUtqX0iq2PUzzBxWpgzPSydw=", | ||||
| 			"path": "github.com/asdine/storm", | ||||
|  | @ -68,12 +74,6 @@ | |||
| 			"revision": "346938d642f2ec3594ed81d874461961cd0faa76", | ||||
| 			"revisionTime": "2016-10-29T20:57:26Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "Xqz4X1+t8MTsmX2guYh7RUj9ZvI=", | ||||
| 			"path": "github.com/edganiukov/fcm", | ||||
| 			"revision": "cbdb173263e8c5ed323ce370b57d1ee5f743b758", | ||||
| 			"revisionTime": "2017-03-11T15:33:24Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "rRYbBQkVsd/+EIryahsywNi34Lo=", | ||||
| 			"path": "github.com/emirpasic/gods/containers", | ||||
|  | @ -164,42 +164,18 @@ | |||
| 			"revision": "553a641470496b2327abcac10b36396bd98e45c9", | ||||
| 			"revisionTime": "2017-02-15T23:32:05Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "oqpLCsrXQdvns4RtmcO61+4BiQk=", | ||||
| 			"path": "github.com/jpillora/backoff", | ||||
| 			"revision": "f24585d1c70490c0920ab34924f54f726e1416c7", | ||||
| 			"revisionTime": "2016-12-23T01:09:21Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "b0T0Hzd+zYk+OCDTFMps+jwa/nY=", | ||||
| 			"path": "github.com/manucorporat/sse", | ||||
| 			"revision": "ee05b128a739a0fb76c7ebd3ae4810c1de808d6d", | ||||
| 			"revisionTime": "2016-01-26T18:01:36Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "Jyv1TH1YHrCVewmVE8FgSh1+92s=", | ||||
| 			"path": "github.com/mattn/go-isatty", | ||||
| 			"revision": "dda3de49cbfcec471bd7a70e6cc01fcc3ff90109", | ||||
| 			"revisionTime": "2017-02-16T23:59:08Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "+ALxEZ4CWoD1Rd6FIN/FFn2NDUU=", | ||||
| 			"path": "github.com/mattn/go-xmpp", | ||||
| 			"revision": "325c112042ffd34aa24365590d3a6fdd1a79c011", | ||||
| 			"revisionTime": "2017-01-28T00:53:20Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "bKMZjd2wPw13VwoE7mBeSv5djFA=", | ||||
| 			"path": "github.com/matttproud/golang_protobuf_extensions/pbutil", | ||||
| 			"revision": "c12348ce28de40eed0136aa2b644d0ee0650e56c", | ||||
| 			"revisionTime": "2016-04-24T11:30:07Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "Se195FlZ160eaEk/uVx4KdTPSxU=", | ||||
| 			"path": "github.com/pborman/uuid", | ||||
| 			"revision": "1b00554d822231195d1babd97ff4a781231955c9", | ||||
| 			"revisionTime": "2017-01-12T15:04:04Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=", | ||||
| 			"path": "github.com/pmezard/go-difflib/difflib", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue