forked from lug/matterbridge
		
	Update vendor lrstanley/girc
This commit is contained in:
		
							
								
								
									
										137
									
								
								vendor/github.com/lrstanley/girc/cap_sasl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								vendor/github.com/lrstanley/girc/cap_sasl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | // Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use | ||||||
|  | // of this source code is governed by the MIT license that can be found in | ||||||
|  | // the LICENSE file. | ||||||
|  |  | ||||||
|  | package girc | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // SASLMech is an representation of what a SASL mechanism should support. | ||||||
|  | // See SASLExternal and SASLPlain for implementations of this. | ||||||
|  | type SASLMech interface { | ||||||
|  | 	// Method returns the uppercase version of the SASL mechanism name. | ||||||
|  | 	Method() string | ||||||
|  | 	// Encode returns the response that the SASL mechanism wants to use. If | ||||||
|  | 	// the returned string is empty (e.g. the mechanism gives up), the handler | ||||||
|  | 	// will attempt to panic, as expectation is that if SASL authentication | ||||||
|  | 	// fails, the client will disconnect. | ||||||
|  | 	Encode(params []string) (output string) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SASLExternal implements the "EXTERNAL" SASL type. | ||||||
|  | type SASLExternal struct { | ||||||
|  | 	// Identity is an optional field which allows the client to specify | ||||||
|  | 	// pre-authentication identification. This means that EXTERNAL will | ||||||
|  | 	// supply this in the initial response. This usually isn't needed (e.g. | ||||||
|  | 	// CertFP). | ||||||
|  | 	Identity string `json:"identity"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Method identifies what type of SASL this implements. | ||||||
|  | func (sasl *SASLExternal) Method() string { | ||||||
|  | 	return "EXTERNAL" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Encode for external SALS authentication should really only return a "+", | ||||||
|  | // unless the user has specified pre-authentication or identification data. | ||||||
|  | // See https://tools.ietf.org/html/rfc4422#appendix-A for more info. | ||||||
|  | func (sasl *SASLExternal) Encode(params []string) string { | ||||||
|  | 	if len(params) != 1 || params[0] != "+" { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if sasl.Identity != "" { | ||||||
|  | 		return sasl.Identity | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return "+" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SASLPlain contains the user and password needed for PLAIN SASL authentication. | ||||||
|  | type SASLPlain struct { | ||||||
|  | 	User string `json:"user"` // User is the username for SASL. | ||||||
|  | 	Pass string `json:"pass"` // Pass is the password for SASL. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Method identifies what type of SASL this implements. | ||||||
|  | func (sasl *SASLPlain) Method() string { | ||||||
|  | 	return "PLAIN" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Encode encodes the plain user+password into a SASL PLAIN implementation. | ||||||
|  | // See https://tools.ietf.org/rfc/rfc4422.txt for more info. | ||||||
|  | func (sasl *SASLPlain) Encode(params []string) string { | ||||||
|  | 	if len(params) != 1 || params[0] != "+" { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	in := []byte(sasl.User) | ||||||
|  |  | ||||||
|  | 	in = append(in, 0x0) | ||||||
|  | 	in = append(in, []byte(sasl.User)...) | ||||||
|  | 	in = append(in, 0x0) | ||||||
|  | 	in = append(in, []byte(sasl.Pass)...) | ||||||
|  |  | ||||||
|  | 	return base64.StdEncoding.EncodeToString(in) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const saslChunkSize = 400 | ||||||
|  |  | ||||||
|  | func handleSASL(c *Client, e Event) { | ||||||
|  | 	if e.Command == RPL_SASLSUCCESS || e.Command == ERR_SASLALREADY { | ||||||
|  | 		// Let the server know that we're done. | ||||||
|  | 		c.write(&Event{Command: CAP, Params: []string{CAP_END}}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Assume they want us to handle sending auth. | ||||||
|  | 	auth := c.Config.SASL.Encode(e.Params) | ||||||
|  |  | ||||||
|  | 	if auth == "" { | ||||||
|  | 		// Assume the SASL authentication method doesn't want to respond for | ||||||
|  | 		// some reason. The SASL spec and IRCv3 spec do not define a clear | ||||||
|  | 		// way to abort a SASL exchange, other than to disconnect, or proceed | ||||||
|  | 		// with CAP END. | ||||||
|  | 		c.rx <- &Event{Command: ERROR, Trailing: fmt.Sprintf( | ||||||
|  | 			"closing connection: SASL %s failed: %s", | ||||||
|  | 			c.Config.SASL.Method(), e.Trailing, | ||||||
|  | 		)} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Send in "saslChunkSize"-length byte chunks. If the last chuck is | ||||||
|  | 	// exactly "saslChunkSize" bytes, send a "AUTHENTICATE +" 0-byte | ||||||
|  | 	// acknowledgement response to let the server know that we're done. | ||||||
|  | 	for { | ||||||
|  | 		if len(auth) > saslChunkSize { | ||||||
|  | 			c.write(&Event{Command: AUTHENTICATE, Params: []string{auth[0 : saslChunkSize-1]}, Sensitive: true}) | ||||||
|  | 			auth = auth[saslChunkSize:] | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if len(auth) <= saslChunkSize { | ||||||
|  | 			c.write(&Event{Command: AUTHENTICATE, Params: []string{auth}, Sensitive: true}) | ||||||
|  |  | ||||||
|  | 			if len(auth) == 400 { | ||||||
|  | 				c.write(&Event{Command: AUTHENTICATE, Params: []string{"+"}}) | ||||||
|  | 			} | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleSASLError(c *Client, e Event) { | ||||||
|  | 	if c.Config.SASL == nil { | ||||||
|  | 		c.write(&Event{Command: CAP, Params: []string{CAP_END}}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Authentication failed. The SASL spec and IRCv3 spec do not define a | ||||||
|  | 	// clear way to abort a SASL exchange, other than to disconnect, or | ||||||
|  | 	// proceed with CAP END. | ||||||
|  | 	c.rx <- &Event{Command: ERROR, Trailing: "closing connection: " + e.Trailing} | ||||||
|  | } | ||||||
							
								
								
									
										318
									
								
								vendor/github.com/lrstanley/girc/cap_tags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								vendor/github.com/lrstanley/girc/cap_tags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,318 @@ | |||||||
|  | // Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use | ||||||
|  | // of this source code is governed by the MIT license that can be found in | ||||||
|  | // the LICENSE file. | ||||||
|  |  | ||||||
|  | package girc | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // handleTags handles any messages that have tags that will affect state. (e.g. | ||||||
|  | // 'account' tags.) | ||||||
|  | func handleTags(c *Client, e Event) { | ||||||
|  | 	if len(e.Tags) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	account, ok := e.Tags.Get("account") | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c.state.Lock() | ||||||
|  | 	user := c.state.lookupUser(e.Source.Name) | ||||||
|  | 	if user != nil { | ||||||
|  | 		user.Extras.Account = account | ||||||
|  | 	} | ||||||
|  | 	c.state.Unlock() | ||||||
|  | 	c.state.notify(c, UPDATE_STATE) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	prefixTag      byte = '@' | ||||||
|  | 	prefixTagValue byte = '=' | ||||||
|  | 	prefixUserTag  byte = '+' | ||||||
|  | 	tagSeparator   byte = ';' | ||||||
|  | 	maxTagLength   int  = 511 // 510 + @ and " " (space), though space usually not included. | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Tags represents the key-value pairs in IRCv3 message tags. The map contains | ||||||
|  | // the encoded message-tag values. If the tag is present, it may still be | ||||||
|  | // empty. See Tags.Get() and Tags.Set() for use with getting/setting | ||||||
|  | // information within the tags. | ||||||
|  | // | ||||||
|  | // Note that retrieving and setting tags are not concurrent safe. If this is | ||||||
|  | // necessary, you will need to implement it yourself. | ||||||
|  | type Tags map[string]string | ||||||
|  |  | ||||||
|  | // ParseTags parses out the key-value map of tags. raw should only be the tag | ||||||
|  | // data, not a full message. For example: | ||||||
|  | //   @aaa=bbb;ccc;example.com/ddd=eee | ||||||
|  | // NOT: | ||||||
|  | //   @aaa=bbb;ccc;example.com/ddd=eee :nick!ident@host.com PRIVMSG me :Hello | ||||||
|  | func ParseTags(raw string) (t Tags) { | ||||||
|  | 	t = make(Tags) | ||||||
|  |  | ||||||
|  | 	if len(raw) > 0 && raw[0] == prefixTag { | ||||||
|  | 		raw = raw[1:] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	parts := strings.Split(raw, string(tagSeparator)) | ||||||
|  | 	var hasValue int | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(parts); i++ { | ||||||
|  | 		hasValue = strings.IndexByte(parts[i], prefixTagValue) | ||||||
|  |  | ||||||
|  | 		// The tag doesn't contain a value or has a splitter with no value. | ||||||
|  | 		if hasValue < 1 || len(parts[i]) < hasValue+1 { | ||||||
|  | 			if !validTag(parts[i]) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			t[parts[i]] = "" | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Check if tag key or decoded value are invalid. | ||||||
|  | 		if !validTag(parts[i][:hasValue]) || !validTagValue(tagDecoder.Replace(parts[i][hasValue+1:])) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		t[parts[i][:hasValue]] = parts[i][hasValue+1:] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return t | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Len determines the length of the bytes representation of this tag map. This | ||||||
|  | // does not include the trailing space required when creating an event, but | ||||||
|  | // does include the tag prefix ("@"). | ||||||
|  | func (t Tags) Len() (length int) { | ||||||
|  | 	if t == nil { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return len(t.Bytes()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Equals compares two Tags for equality. With the msgid IRCv3 spec +\ | ||||||
|  | // echo-message (amongst others), we may receive events that have msgid's, | ||||||
|  | // whereas our local events will not have the msgid. As such, don't compare | ||||||
|  | // all tags, only the necessary/important tags. | ||||||
|  | func (t Tags) Equals(tt Tags) bool { | ||||||
|  | 	// The only tag which is important at this time. | ||||||
|  | 	taccount, _ := t.Get("account") | ||||||
|  | 	ttaccount, _ := tt.Get("account") | ||||||
|  | 	return taccount == ttaccount | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Keys returns a slice of (unsorted) tag keys. | ||||||
|  | func (t Tags) Keys() (keys []string) { | ||||||
|  | 	keys = make([]string, 0, t.Count()) | ||||||
|  | 	for key := range t { | ||||||
|  | 		keys = append(keys, key) | ||||||
|  | 	} | ||||||
|  | 	return keys | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Count finds how many total tags that there are. | ||||||
|  | func (t Tags) Count() int { | ||||||
|  | 	if t == nil { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return len(t) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Bytes returns a []byte representation of this tag map, including the tag | ||||||
|  | // prefix ("@"). Note that this will return the tags sorted, regardless of | ||||||
|  | // the order of how they were originally parsed. | ||||||
|  | func (t Tags) Bytes() []byte { | ||||||
|  | 	if t == nil { | ||||||
|  | 		return []byte{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	max := len(t) | ||||||
|  | 	if max == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buffer := new(bytes.Buffer) | ||||||
|  | 	buffer.WriteByte(prefixTag) | ||||||
|  |  | ||||||
|  | 	var current int | ||||||
|  |  | ||||||
|  | 	// Sort the writing of tags so we can at least guarantee that they will | ||||||
|  | 	// be in order, and testable. | ||||||
|  | 	var names []string | ||||||
|  | 	for tagName := range t { | ||||||
|  | 		names = append(names, tagName) | ||||||
|  | 	} | ||||||
|  | 	sort.Strings(names) | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(names); i++ { | ||||||
|  | 		// Trim at max allowed chars. | ||||||
|  | 		if (buffer.Len() + len(names[i]) + len(t[names[i]]) + 2) > maxTagLength { | ||||||
|  | 			return buffer.Bytes() | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		buffer.WriteString(names[i]) | ||||||
|  |  | ||||||
|  | 		// Write the value as necessary. | ||||||
|  | 		if len(t[names[i]]) > 0 { | ||||||
|  | 			buffer.WriteByte(prefixTagValue) | ||||||
|  | 			buffer.WriteString(t[names[i]]) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// add the separator ";" between tags. | ||||||
|  | 		if current < max-1 { | ||||||
|  | 			buffer.WriteByte(tagSeparator) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		current++ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buffer.Bytes() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // String returns a string representation of this tag map. | ||||||
|  | func (t Tags) String() string { | ||||||
|  | 	if t == nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return string(t.Bytes()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // writeTo writes the necessary tag bytes to an io.Writer, including a trailing | ||||||
|  | // space-separator. | ||||||
|  | func (t Tags) writeTo(w io.Writer) (n int, err error) { | ||||||
|  | 	b := t.Bytes() | ||||||
|  | 	if len(b) == 0 { | ||||||
|  | 		return n, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n, err = w.Write(b) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return n, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var j int | ||||||
|  | 	j, err = w.Write([]byte{eventSpace}) | ||||||
|  | 	n += j | ||||||
|  |  | ||||||
|  | 	return n, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // tagDecode are encoded -> decoded pairs for replacement to decode. | ||||||
|  | var tagDecode = []string{ | ||||||
|  | 	"\\:", ";", | ||||||
|  | 	"\\s", " ", | ||||||
|  | 	"\\\\", "\\", | ||||||
|  | 	"\\r", "\r", | ||||||
|  | 	"\\n", "\n", | ||||||
|  | } | ||||||
|  | var tagDecoder = strings.NewReplacer(tagDecode...) | ||||||
|  |  | ||||||
|  | // tagEncode are decoded -> encoded pairs for replacement to decode. | ||||||
|  | var tagEncode = []string{ | ||||||
|  | 	";", "\\:", | ||||||
|  | 	" ", "\\s", | ||||||
|  | 	"\\", "\\\\", | ||||||
|  | 	"\r", "\\r", | ||||||
|  | 	"\n", "\\n", | ||||||
|  | } | ||||||
|  | var tagEncoder = strings.NewReplacer(tagEncode...) | ||||||
|  |  | ||||||
|  | // Get returns the unescaped value of given tag key. Note that this is not | ||||||
|  | // concurrent safe. | ||||||
|  | func (t Tags) Get(key string) (tag string, success bool) { | ||||||
|  | 	if t == nil { | ||||||
|  | 		return "", false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, ok := t[key]; ok { | ||||||
|  | 		tag = tagDecoder.Replace(t[key]) | ||||||
|  | 		success = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return tag, success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set escapes given value and saves it as the value for given key. Note that | ||||||
|  | // this is not concurrent safe. | ||||||
|  | func (t Tags) Set(key, value string) error { | ||||||
|  | 	if t == nil { | ||||||
|  | 		t = make(Tags) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !validTag(key) { | ||||||
|  | 		return fmt.Errorf("tag key %q is invalid", key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	value = tagEncoder.Replace(value) | ||||||
|  |  | ||||||
|  | 	if len(value) > 0 && !validTagValue(value) { | ||||||
|  | 		return fmt.Errorf("tag value %q of key %q is invalid", value, key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check to make sure it's not too long here. | ||||||
|  | 	if (t.Len() + len(key) + len(value) + 2) > maxTagLength { | ||||||
|  | 		return fmt.Errorf("unable to set tag %q [value %q]: tags too long for message", key, value) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t[key] = value | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Remove deletes the tag frwom the tag map. | ||||||
|  | func (t Tags) Remove(key string) (success bool) { | ||||||
|  | 	if t == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, success = t[key]; success { | ||||||
|  | 		delete(t, key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return success | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // validTag validates an IRC tag. | ||||||
|  | func validTag(name string) bool { | ||||||
|  | 	if len(name) < 1 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Allow user tags to be passed to validTag. | ||||||
|  | 	if len(name) >= 2 && name[0] == prefixUserTag { | ||||||
|  | 		name = name[1:] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(name); i++ { | ||||||
|  | 		// A-Z, a-z, 0-9, -/._ | ||||||
|  | 		if (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '-' || name[i] > '9') && name[i] != '_' { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // validTagValue valids a decoded IRC tag value. If the value is not decoded | ||||||
|  | // with tagDecoder first, it may be seen as invalid. | ||||||
|  | func validTagValue(value string) bool { | ||||||
|  | 	for i := 0; i < len(value); i++ { | ||||||
|  | 		// Don't allow any invisible chars within the tag, or semicolons. | ||||||
|  | 		if value[i] < '!' || value[i] > '~' || value[i] == ';' { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
							
								
								
									
										338
									
								
								vendor/github.com/lrstanley/girc/constants.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								vendor/github.com/lrstanley/girc/constants.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | |||||||
|  | // Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use | ||||||
|  | // of this source code is governed by the MIT license that can be found in | ||||||
|  | // the LICENSE file. | ||||||
|  |  | ||||||
|  | package girc | ||||||
|  |  | ||||||
|  | // Standard CTCP based constants. | ||||||
|  | const ( | ||||||
|  | 	CTCP_PING       = "PING" | ||||||
|  | 	CTCP_PONG       = "PONG" | ||||||
|  | 	CTCP_VERSION    = "VERSION" | ||||||
|  | 	CTCP_USERINFO   = "USERINFO" | ||||||
|  | 	CTCP_CLIENTINFO = "CLIENTINFO" | ||||||
|  | 	CTCP_SOURCE     = "SOURCE" | ||||||
|  | 	CTCP_TIME       = "TIME" | ||||||
|  | 	CTCP_FINGER     = "FINGER" | ||||||
|  | 	CTCP_ERRMSG     = "ERRMSG" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Emulated event commands used to allow easier hooks into the changing | ||||||
|  | // state of the client. | ||||||
|  | const ( | ||||||
|  | 	UPDATE_STATE   = "CLIENT_STATE_UPDATED"   // when channel/user state is updated. | ||||||
|  | 	UPDATE_GENERAL = "CLIENT_GENERAL_UPDATED" // when general state (client nick, server name, etc) is updated. | ||||||
|  | 	ALL_EVENTS     = "*"                      // trigger on all events | ||||||
|  | 	CONNECTED      = "CLIENT_CONNECTED"       // when it's safe to send arbitrary commands (joins, list, who, etc), trailing is host:port | ||||||
|  | 	INITIALIZED    = "CLIENT_INIT"            // verifies successful socket connection, trailing is host:port | ||||||
|  | 	DISCONNECTED   = "CLIENT_DISCONNECTED"    // occurs when we're disconnected from the server (user-requested or not) | ||||||
|  | 	STOPPED        = "CLIENT_STOPPED"         // occurs when Client.Stop() has been called | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // User/channel prefixes :: RFC1459. | ||||||
|  | const ( | ||||||
|  | 	DefaultPrefixes = "(ov)@+" // the most common default prefixes | ||||||
|  | 	ModeAddPrefix   = "+"      // modes are being added | ||||||
|  | 	ModeDelPrefix   = "-"      // modes are being removed | ||||||
|  |  | ||||||
|  | 	ChannelPrefix      = "#" // regular channel | ||||||
|  | 	DistributedPrefix  = "&" // distributed channel | ||||||
|  | 	OwnerPrefix        = "~" // user owner +q (non-rfc) | ||||||
|  | 	AdminPrefix        = "&" // user admin +a (non-rfc) | ||||||
|  | 	HalfOperatorPrefix = "%" // user half operator +h (non-rfc) | ||||||
|  | 	OperatorPrefix     = "@" // user operator +o | ||||||
|  | 	VoicePrefix        = "+" // user has voice +v | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // User modes :: RFC1459; section 4.2.3.2. | ||||||
|  | const ( | ||||||
|  | 	UserModeInvisible     = "i" // invisible | ||||||
|  | 	UserModeOperator      = "o" // server operator | ||||||
|  | 	UserModeServerNotices = "s" // user wants to receive server notices | ||||||
|  | 	UserModeWallops       = "w" // user wants to receive wallops | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Channel modes :: RFC1459; section 4.2.3.1. | ||||||
|  | const ( | ||||||
|  | 	ModeDefaults = "beI,k,l,imnpst" // the most common default modes | ||||||
|  |  | ||||||
|  | 	ModeInviteOnly = "i" // only join with an invite | ||||||
|  | 	ModeKey        = "k" // channel password | ||||||
|  | 	ModeLimit      = "l" // user limit | ||||||
|  | 	ModeModerated  = "m" // only voiced users and operators can talk | ||||||
|  | 	ModeOperator   = "o" // operator | ||||||
|  | 	ModePrivate    = "p" // private | ||||||
|  | 	ModeSecret     = "s" // secret | ||||||
|  | 	ModeTopic      = "t" // must be op to set topic | ||||||
|  | 	ModeVoice      = "v" // speak during moderation mode | ||||||
|  |  | ||||||
|  | 	ModeOwner        = "q" // owner privileges (non-rfc) | ||||||
|  | 	ModeAdmin        = "a" // admin privileges (non-rfc) | ||||||
|  | 	ModeHalfOperator = "h" // half-operator privileges (non-rfc) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IRC commands :: RFC2812; section 3 :: RFC2813; section 4. | ||||||
|  | const ( | ||||||
|  | 	ADMIN    = "ADMIN" | ||||||
|  | 	AWAY     = "AWAY" | ||||||
|  | 	CONNECT  = "CONNECT" | ||||||
|  | 	DIE      = "DIE" | ||||||
|  | 	ERROR    = "ERROR" | ||||||
|  | 	INFO     = "INFO" | ||||||
|  | 	INVITE   = "INVITE" | ||||||
|  | 	ISON     = "ISON" | ||||||
|  | 	JOIN     = "JOIN" | ||||||
|  | 	KICK     = "KICK" | ||||||
|  | 	KILL     = "KILL" | ||||||
|  | 	LINKS    = "LINKS" | ||||||
|  | 	LIST     = "LIST" | ||||||
|  | 	LUSERS   = "LUSERS" | ||||||
|  | 	MODE     = "MODE" | ||||||
|  | 	MOTD     = "MOTD" | ||||||
|  | 	NAMES    = "NAMES" | ||||||
|  | 	NICK     = "NICK" | ||||||
|  | 	NJOIN    = "NJOIN" | ||||||
|  | 	NOTICE   = "NOTICE" | ||||||
|  | 	OPER     = "OPER" | ||||||
|  | 	PART     = "PART" | ||||||
|  | 	PASS     = "PASS" | ||||||
|  | 	PING     = "PING" | ||||||
|  | 	PONG     = "PONG" | ||||||
|  | 	PRIVMSG  = "PRIVMSG" | ||||||
|  | 	QUIT     = "QUIT" | ||||||
|  | 	REHASH   = "REHASH" | ||||||
|  | 	RESTART  = "RESTART" | ||||||
|  | 	SERVER   = "SERVER" | ||||||
|  | 	SERVICE  = "SERVICE" | ||||||
|  | 	SERVLIST = "SERVLIST" | ||||||
|  | 	SQUERY   = "SQUERY" | ||||||
|  | 	SQUIT    = "SQUIT" | ||||||
|  | 	STATS    = "STATS" | ||||||
|  | 	SUMMON   = "SUMMON" | ||||||
|  | 	TIME     = "TIME" | ||||||
|  | 	TOPIC    = "TOPIC" | ||||||
|  | 	TRACE    = "TRACE" | ||||||
|  | 	USER     = "USER" | ||||||
|  | 	USERHOST = "USERHOST" | ||||||
|  | 	USERS    = "USERS" | ||||||
|  | 	VERSION  = "VERSION" | ||||||
|  | 	WALLOPS  = "WALLOPS" | ||||||
|  | 	WHO      = "WHO" | ||||||
|  | 	WHOIS    = "WHOIS" | ||||||
|  | 	WHOWAS   = "WHOWAS" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Numeric IRC reply mapping :: RFC2812; section 5. | ||||||
|  | const ( | ||||||
|  | 	RPL_WELCOME           = "001" | ||||||
|  | 	RPL_YOURHOST          = "002" | ||||||
|  | 	RPL_CREATED           = "003" | ||||||
|  | 	RPL_MYINFO            = "004" | ||||||
|  | 	RPL_BOUNCE            = "005" | ||||||
|  | 	RPL_ISUPPORT          = "005" | ||||||
|  | 	RPL_USERHOST          = "302" | ||||||
|  | 	RPL_ISON              = "303" | ||||||
|  | 	RPL_AWAY              = "301" | ||||||
|  | 	RPL_UNAWAY            = "305" | ||||||
|  | 	RPL_NOWAWAY           = "306" | ||||||
|  | 	RPL_WHOISUSER         = "311" | ||||||
|  | 	RPL_WHOISSERVER       = "312" | ||||||
|  | 	RPL_WHOISOPERATOR     = "313" | ||||||
|  | 	RPL_WHOISIDLE         = "317" | ||||||
|  | 	RPL_ENDOFWHOIS        = "318" | ||||||
|  | 	RPL_WHOISCHANNELS     = "319" | ||||||
|  | 	RPL_WHOWASUSER        = "314" | ||||||
|  | 	RPL_ENDOFWHOWAS       = "369" | ||||||
|  | 	RPL_LISTSTART         = "321" | ||||||
|  | 	RPL_LIST              = "322" | ||||||
|  | 	RPL_LISTEND           = "323" | ||||||
|  | 	RPL_UNIQOPIS          = "325" | ||||||
|  | 	RPL_CHANNELMODEIS     = "324" | ||||||
|  | 	RPL_NOTOPIC           = "331" | ||||||
|  | 	RPL_TOPIC             = "332" | ||||||
|  | 	RPL_INVITING          = "341" | ||||||
|  | 	RPL_SUMMONING         = "342" | ||||||
|  | 	RPL_INVITELIST        = "346" | ||||||
|  | 	RPL_ENDOFINVITELIST   = "347" | ||||||
|  | 	RPL_EXCEPTLIST        = "348" | ||||||
|  | 	RPL_ENDOFEXCEPTLIST   = "349" | ||||||
|  | 	RPL_VERSION           = "351" | ||||||
|  | 	RPL_WHOREPLY          = "352" | ||||||
|  | 	RPL_ENDOFWHO          = "315" | ||||||
|  | 	RPL_NAMREPLY          = "353" | ||||||
|  | 	RPL_ENDOFNAMES        = "366" | ||||||
|  | 	RPL_LINKS             = "364" | ||||||
|  | 	RPL_ENDOFLINKS        = "365" | ||||||
|  | 	RPL_BANLIST           = "367" | ||||||
|  | 	RPL_ENDOFBANLIST      = "368" | ||||||
|  | 	RPL_INFO              = "371" | ||||||
|  | 	RPL_ENDOFINFO         = "374" | ||||||
|  | 	RPL_MOTDSTART         = "375" | ||||||
|  | 	RPL_MOTD              = "372" | ||||||
|  | 	RPL_ENDOFMOTD         = "376" | ||||||
|  | 	RPL_YOUREOPER         = "381" | ||||||
|  | 	RPL_REHASHING         = "382" | ||||||
|  | 	RPL_YOURESERVICE      = "383" | ||||||
|  | 	RPL_TIME              = "391" | ||||||
|  | 	RPL_USERSSTART        = "392" | ||||||
|  | 	RPL_USERS             = "393" | ||||||
|  | 	RPL_ENDOFUSERS        = "394" | ||||||
|  | 	RPL_NOUSERS           = "395" | ||||||
|  | 	RPL_TRACELINK         = "200" | ||||||
|  | 	RPL_TRACECONNECTING   = "201" | ||||||
|  | 	RPL_TRACEHANDSHAKE    = "202" | ||||||
|  | 	RPL_TRACEUNKNOWN      = "203" | ||||||
|  | 	RPL_TRACEOPERATOR     = "204" | ||||||
|  | 	RPL_TRACEUSER         = "205" | ||||||
|  | 	RPL_TRACESERVER       = "206" | ||||||
|  | 	RPL_TRACESERVICE      = "207" | ||||||
|  | 	RPL_TRACENEWTYPE      = "208" | ||||||
|  | 	RPL_TRACECLASS        = "209" | ||||||
|  | 	RPL_TRACERECONNECT    = "210" | ||||||
|  | 	RPL_TRACELOG          = "261" | ||||||
|  | 	RPL_TRACEEND          = "262" | ||||||
|  | 	RPL_STATSLINKINFO     = "211" | ||||||
|  | 	RPL_STATSCOMMANDS     = "212" | ||||||
|  | 	RPL_ENDOFSTATS        = "219" | ||||||
|  | 	RPL_STATSUPTIME       = "242" | ||||||
|  | 	RPL_STATSOLINE        = "243" | ||||||
|  | 	RPL_UMODEIS           = "221" | ||||||
|  | 	RPL_SERVLIST          = "234" | ||||||
|  | 	RPL_SERVLISTEND       = "235" | ||||||
|  | 	RPL_LUSERCLIENT       = "251" | ||||||
|  | 	RPL_LUSEROP           = "252" | ||||||
|  | 	RPL_LUSERUNKNOWN      = "253" | ||||||
|  | 	RPL_LUSERCHANNELS     = "254" | ||||||
|  | 	RPL_LUSERME           = "255" | ||||||
|  | 	RPL_ADMINME           = "256" | ||||||
|  | 	RPL_ADMINLOC1         = "257" | ||||||
|  | 	RPL_ADMINLOC2         = "258" | ||||||
|  | 	RPL_ADMINEMAIL        = "259" | ||||||
|  | 	RPL_TRYAGAIN          = "263" | ||||||
|  | 	ERR_NOSUCHNICK        = "401" | ||||||
|  | 	ERR_NOSUCHSERVER      = "402" | ||||||
|  | 	ERR_NOSUCHCHANNEL     = "403" | ||||||
|  | 	ERR_CANNOTSENDTOCHAN  = "404" | ||||||
|  | 	ERR_TOOMANYCHANNELS   = "405" | ||||||
|  | 	ERR_WASNOSUCHNICK     = "406" | ||||||
|  | 	ERR_TOOMANYTARGETS    = "407" | ||||||
|  | 	ERR_NOSUCHSERVICE     = "408" | ||||||
|  | 	ERR_NOORIGIN          = "409" | ||||||
|  | 	ERR_NORECIPIENT       = "411" | ||||||
|  | 	ERR_NOTEXTTOSEND      = "412" | ||||||
|  | 	ERR_NOTOPLEVEL        = "413" | ||||||
|  | 	ERR_WILDTOPLEVEL      = "414" | ||||||
|  | 	ERR_BADMASK           = "415" | ||||||
|  | 	ERR_UNKNOWNCOMMAND    = "421" | ||||||
|  | 	ERR_NOMOTD            = "422" | ||||||
|  | 	ERR_NOADMININFO       = "423" | ||||||
|  | 	ERR_FILEERROR         = "424" | ||||||
|  | 	ERR_NONICKNAMEGIVEN   = "431" | ||||||
|  | 	ERR_ERRONEUSNICKNAME  = "432" | ||||||
|  | 	ERR_NICKNAMEINUSE     = "433" | ||||||
|  | 	ERR_NICKCOLLISION     = "436" | ||||||
|  | 	ERR_UNAVAILRESOURCE   = "437" | ||||||
|  | 	ERR_USERNOTINCHANNEL  = "441" | ||||||
|  | 	ERR_NOTONCHANNEL      = "442" | ||||||
|  | 	ERR_USERONCHANNEL     = "443" | ||||||
|  | 	ERR_NOLOGIN           = "444" | ||||||
|  | 	ERR_SUMMONDISABLED    = "445" | ||||||
|  | 	ERR_USERSDISABLED     = "446" | ||||||
|  | 	ERR_NOTREGISTERED     = "451" | ||||||
|  | 	ERR_NEEDMOREPARAMS    = "461" | ||||||
|  | 	ERR_ALREADYREGISTRED  = "462" | ||||||
|  | 	ERR_NOPERMFORHOST     = "463" | ||||||
|  | 	ERR_PASSWDMISMATCH    = "464" | ||||||
|  | 	ERR_YOUREBANNEDCREEP  = "465" | ||||||
|  | 	ERR_YOUWILLBEBANNED   = "466" | ||||||
|  | 	ERR_KEYSET            = "467" | ||||||
|  | 	ERR_CHANNELISFULL     = "471" | ||||||
|  | 	ERR_UNKNOWNMODE       = "472" | ||||||
|  | 	ERR_INVITEONLYCHAN    = "473" | ||||||
|  | 	ERR_BANNEDFROMCHAN    = "474" | ||||||
|  | 	ERR_BADCHANNELKEY     = "475" | ||||||
|  | 	ERR_BADCHANMASK       = "476" | ||||||
|  | 	ERR_NOCHANMODES       = "477" | ||||||
|  | 	ERR_BANLISTFULL       = "478" | ||||||
|  | 	ERR_NOPRIVILEGES      = "481" | ||||||
|  | 	ERR_CHANOPRIVSNEEDED  = "482" | ||||||
|  | 	ERR_CANTKILLSERVER    = "483" | ||||||
|  | 	ERR_RESTRICTED        = "484" | ||||||
|  | 	ERR_UNIQOPPRIVSNEEDED = "485" | ||||||
|  | 	ERR_NOOPERHOST        = "491" | ||||||
|  | 	ERR_UMODEUNKNOWNFLAG  = "501" | ||||||
|  | 	ERR_USERSDONTMATCH    = "502" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IRCv3 commands and extensions :: http://ircv3.net/irc/. | ||||||
|  | const ( | ||||||
|  | 	AUTHENTICATE = "AUTHENTICATE" | ||||||
|  | 	STARTTLS     = "STARTTLS" | ||||||
|  |  | ||||||
|  | 	CAP       = "CAP" | ||||||
|  | 	CAP_ACK   = "ACK" | ||||||
|  | 	CAP_CLEAR = "CLEAR" | ||||||
|  | 	CAP_END   = "END" | ||||||
|  | 	CAP_LIST  = "LIST" | ||||||
|  | 	CAP_LS    = "LS" | ||||||
|  | 	CAP_NAK   = "NAK" | ||||||
|  | 	CAP_REQ   = "REQ" | ||||||
|  | 	CAP_NEW   = "NEW" | ||||||
|  | 	CAP_DEL   = "DEL" | ||||||
|  |  | ||||||
|  | 	CAP_CHGHOST = "CHGHOST" | ||||||
|  | 	CAP_AWAY    = "AWAY" | ||||||
|  | 	CAP_ACCOUNT = "ACCOUNT" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Numeric IRC reply mapping for ircv3 :: http://ircv3.net/irc/. | ||||||
|  | const ( | ||||||
|  | 	RPL_LOGGEDIN    = "900" | ||||||
|  | 	RPL_LOGGEDOUT   = "901" | ||||||
|  | 	RPL_NICKLOCKED  = "902" | ||||||
|  | 	RPL_SASLSUCCESS = "903" | ||||||
|  | 	ERR_SASLFAIL    = "904" | ||||||
|  | 	ERR_SASLTOOLONG = "905" | ||||||
|  | 	ERR_SASLABORTED = "906" | ||||||
|  | 	ERR_SASLALREADY = "907" | ||||||
|  | 	RPL_SASLMECHS   = "908" | ||||||
|  | 	RPL_STARTTLS    = "670" | ||||||
|  | 	ERR_STARTTLS    = "691" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Numeric IRC event mapping :: RFC2812; section 5.3. | ||||||
|  | const ( | ||||||
|  | 	RPL_STATSCLINE    = "213" | ||||||
|  | 	RPL_STATSNLINE    = "214" | ||||||
|  | 	RPL_STATSILINE    = "215" | ||||||
|  | 	RPL_STATSKLINE    = "216" | ||||||
|  | 	RPL_STATSQLINE    = "217" | ||||||
|  | 	RPL_STATSYLINE    = "218" | ||||||
|  | 	RPL_SERVICEINFO   = "231" | ||||||
|  | 	RPL_ENDOFSERVICES = "232" | ||||||
|  | 	RPL_SERVICE       = "233" | ||||||
|  | 	RPL_STATSVLINE    = "240" | ||||||
|  | 	RPL_STATSLLINE    = "241" | ||||||
|  | 	RPL_STATSHLINE    = "244" | ||||||
|  | 	RPL_STATSSLINE    = "245" | ||||||
|  | 	RPL_STATSPING     = "246" | ||||||
|  | 	RPL_STATSBLINE    = "247" | ||||||
|  | 	RPL_STATSDLINE    = "250" | ||||||
|  | 	RPL_NONE          = "300" | ||||||
|  | 	RPL_WHOISCHANOP   = "316" | ||||||
|  | 	RPL_KILLDONE      = "361" | ||||||
|  | 	RPL_CLOSING       = "362" | ||||||
|  | 	RPL_CLOSEEND      = "363" | ||||||
|  | 	RPL_INFOSTART     = "373" | ||||||
|  | 	RPL_MYPORTIS      = "384" | ||||||
|  | 	ERR_NOSERVICEHOST = "492" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Misc. | ||||||
|  | const ( | ||||||
|  | 	ERR_TOOMANYMATCHES = "416" // IRCNet. | ||||||
|  | 	RPL_GLOBALUSERS    = "266" // aircd/hybrid/bahamut, used on freenode. | ||||||
|  | 	RPL_LOCALUSERS     = "265" // aircd/hybrid/bahamut, used on freenode. | ||||||
|  | 	RPL_TOPICWHOTIME   = "333" // ircu, used on freenode. | ||||||
|  | 	RPL_WHOSPCRPL      = "354" // ircu, used on networks with WHOX support. | ||||||
|  | ) | ||||||
		Reference in New Issue
	
	Block a user
	 Wim
					Wim