commit 528fd03f6fede9b68d2d1888e28b3a78ba478201 Author: Trevor Slocum Date: Thu Aug 25 22:50:24 2016 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64bebaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +anonircd diff --git a/README b/README new file mode 100644 index 0000000..961fa14 --- /dev/null +++ b/README @@ -0,0 +1 @@ +how do i shot web diff --git a/anonircd.go b/anonircd.go new file mode 100644 index 0000000..756baeb --- /dev/null +++ b/anonircd.go @@ -0,0 +1,118 @@ +package main + +import ( + "net" + "log" + "sync" + "math/rand" + irc "gopkg.in/sorcix/irc.v2" + "fmt" + "time" +) + +const ( + MSG_RAW = 0 + MSG_COMMAND = 1 + MSG_PING = 2 +) + +type Channel struct { + clients Client +} + +type Client struct { + identifier string + conn net.Conn + + writebuffer chan *irc.Message + + reader *irc.Decoder + writer *irc.Encoder +} +/* +func (c *Client) Send(message Message) { + c.conn.wr <- message +}*/ + +type Server struct { + sync.Mutex + channels map[string]Channel + clients map[string]*Client +} + +const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func randomIdentifier() string { + b := make([]byte, 10) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func handleRead(c *Client, server *Server) { + for { + c.conn.SetDeadline(time.Now().Add(300 * time.Second)) + msg, err := c.reader.Decode() + if err != nil { + //return c.Reconnect() + } + fmt.Println("%#v", msg) + + fmt.Println("PREFIX: " + fmt.Sprintf("%v", msg.Prefix)) + fmt.Println("COMMAND: " + fmt.Sprintf("%v", msg.Command)) + fmt.Println("PARAMS: " + fmt.Sprintf("%v", msg.Params)) + if (msg.Command == irc.CAP && len(msg.Params) > 0 && (msg.Params[0] == irc.CAP_LS || msg.Params[0] == irc.CAP_LIST)) { + fmt.Println("WAS CAP") + response := irc.Message{nil, irc.CAP, []string{"*", msg.Params[0], ""}} + c.writebuffer <- &response + } + + prfx := irc.Prefix{Name: "tee"} + for _, sclient := range server.clients { + msgout := irc.Message{&prfx, irc.PRIVMSG, []string{"#test", msg.Trailing()}} + sclient.writebuffer <- &msgout + } + } +} + +func handleWrite(c *Client, server *Server) { + for msg := range c.writebuffer { + c.writer.Encode(msg) + } +} + +func handleConnection(conn net.Conn, server *Server) { + messages := make(chan *irc.Message) + client := Client{randomIdentifier(), conn, messages, irc.NewDecoder(conn), irc.NewEncoder(conn)} + + server.Lock() + server.clients[client.identifier] = &client + server.Unlock() + + defer conn.Close() + go handleRead(&client, server) + handleWrite(&client, server) +} + +func listen(server *Server) { + ln, err := net.Listen("tcp", ":6667") + if err != nil { + log.Fatalf("Failed to listen: %v", err) + } + + for { + conn, err := ln.Accept() + if err != nil { + continue + } + go handleConnection(conn, server) + } +} + +func main() { + server := Server{ + clients: make(map[string]*Client), + channels: make(map[string]Channel)} + listen(&server) +} \ No newline at end of file diff --git a/vendor/gopkg.in/sorcix/irc.v2/LICENSE b/vendor/gopkg.in/sorcix/irc.v2/LICENSE new file mode 100644 index 0000000..10cecc4 --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/LICENSE @@ -0,0 +1,22 @@ +Copyright 2014 Vic Demuzere + +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. diff --git a/vendor/gopkg.in/sorcix/irc.v2/README.md b/vendor/gopkg.in/sorcix/irc.v2/README.md new file mode 100644 index 0000000..496e5fb --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/README.md @@ -0,0 +1,60 @@ +# Go **irc** package + +[![Build Status](https://travis-ci.org/sorcix/irc.svg?branch=v2)](https://travis-ci.org/sorcix/irc) +[![GoDoc](https://godoc.org/gopkg.in/sorcix/irc.v2?status.svg)](https://godoc.org/gopkg.in/sorcix/irc.v2) + +## Features +Package irc allows your application to speak the IRC protocol. + + - **Limited scope**, does one thing and does it well. + - Focus on simplicity and **speed**. + - **Stable API**: updates shouldn't break existing software. + - Well [documented][Documentation] code. + +*This package does not manage your entire IRC connection. It only translates the protocol to easy to use Go types. It is meant as a single component in a larger IRC library, or for basic IRC bots for which a large IRC package would be overkill.* + +## Usage + +``` +import "gopkg.in/sorcix/irc.v2" +``` + +### Message +The [Message][] and [Prefix][] types provide translation to and from IRC message format. + + // Parse the IRC-encoded data and stores the result in a new struct. + message := irc.ParseMessage(raw) + + // Returns the IRC encoding of the message. + raw = message.String() + +### Encoder & Decoder +The [Encoder][] and [Decoder][] types allow working with IRC message streams. + + // Create a decoder that reads from given io.Reader + dec := irc.NewDecoder(reader) + + // Decode the next IRC message + message, err := dec.Decode() + + // Create an encoder that writes to given io.Writer + enc := irc.NewEncoder(writer) + + // Send a message to the writer. + enc.Encode(message) + +### Conn +The [Conn][] type combines an [Encoder][] and [Decoder][] for a duplex connection. + + c, err := irc.Dial("irc.server.net:6667") + + // Methods from both Encoder and Decoder are available + message, err := c.Decode() + +[Documentation]: https://godoc.org/gopkg.in/sorcix/irc.v2 "Package documentation by Godoc.org" +[Message]: https://godoc.org/gopkg.in/sorcix/irc.v2#Message "Message type documentation" +[Prefix]: https://godoc.org/gopkg.in/sorcix/irc.v2#Prefix "Prefix type documentation" +[Encoder]: https://godoc.org/gopkg.in/sorcix/irc.v2#Encoder "Encoder type documentation" +[Decoder]: https://godoc.org/gopkg.in/sorcix/irc.v2#Decoder "Decoder type documentation" +[Conn]: https://godoc.org/gopkg.in/sorcix/irc.v2#Conn "Conn type documentation" +[RFC1459]: https://tools.ietf.org/html/rfc1459.html "RFC 1459" diff --git a/vendor/gopkg.in/sorcix/irc.v2/constants.go b/vendor/gopkg.in/sorcix/irc.v2/constants.go new file mode 100644 index 0000000..d4812ba --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/constants.go @@ -0,0 +1,298 @@ +// Copyright 2014 Vic Demuzere +// +// Use of this source code is governed by the MIT license. + +package irc + +// Various prefixes extracted from RFC1459. +const ( + Channel = '#' // Normal channel + Distributed = '&' // Distributed channel + + Owner = '~' // Channel owner +q (non-standard) + Admin = '&' // Channel admin +a (non-standard) + Operator = '@' // Channel operator +o + HalfOperator = '%' // Channel half operator +h (non-standard) + Voice = '+' // User has voice +v +) + +// User modes as defined by RFC1459 section 4.2.3.2. +const ( + UserModeInvisible = 'i' // User is invisible + UserModeServerNotices = 's' // User wants to receive server notices + UserModeWallops = 'w' // User wants to receive Wallops + UserModeOperator = 'o' // Server operator +) + +// Channel modes as defined by RFC1459 section 4.2.3.1 +const ( + ModeOperator = 'o' // Operator privileges + ModeVoice = 'v' // Ability to speak on a moderated channel + ModePrivate = 'p' // Private channel + ModeSecret = 's' // Secret channel + ModeInviteOnly = 'i' // Users can't join without invite + ModeTopic = 't' // Topic can only be set by an operator + ModeModerated = 'm' // Only voiced users and operators can talk + ModeLimit = 'l' // User limit + ModeKey = 'k' // Channel password + + ModeOwner = 'q' // Owner privileges (non-standard) + ModeAdmin = 'a' // Admin privileges (non-standard) + ModeHalfOperator = 'h' // Half-operator privileges (non-standard) +) + +// IRC commands extracted from RFC2812 section 3 and RFC2813 section 4. +const ( + PASS = "PASS" + NICK = "NICK" + USER = "USER" + OPER = "OPER" + MODE = "MODE" + SERVICE = "SERVICE" + QUIT = "QUIT" + SQUIT = "SQUIT" + JOIN = "JOIN" + PART = "PART" + TOPIC = "TOPIC" + NAMES = "NAMES" + LIST = "LIST" + INVITE = "INVITE" + KICK = "KICK" + PRIVMSG = "PRIVMSG" + NOTICE = "NOTICE" + MOTD = "MOTD" + LUSERS = "LUSERS" + VERSION = "VERSION" + STATS = "STATS" + LINKS = "LINKS" + TIME = "TIME" + CONNECT = "CONNECT" + TRACE = "TRACE" + ADMIN = "ADMIN" + INFO = "INFO" + SERVLIST = "SERVLIST" + SQUERY = "SQUERY" + WHO = "WHO" + WHOIS = "WHOIS" + WHOWAS = "WHOWAS" + KILL = "KILL" + PING = "PING" + PONG = "PONG" + ERROR = "ERROR" + AWAY = "AWAY" + REHASH = "REHASH" + DIE = "DIE" + RESTART = "RESTART" + SUMMON = "SUMMON" + USERS = "USERS" + WALLOPS = "WALLOPS" + USERHOST = "USERHOST" + ISON = "ISON" + SERVER = "SERVER" + NJOIN = "NJOIN" +) + +// Numeric IRC replies extracted from 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" +) + +// IRC commands extracted from the IRCv3 spec at http://www.ircv3.org/. +const ( + CAP = "CAP" + CAP_LS = "LS" // Subcommand (param) + CAP_LIST = "LIST" // Subcommand (param) + CAP_REQ = "REQ" // Subcommand (param) + CAP_ACK = "ACK" // Subcommand (param) + CAP_NAK = "NAK" // Subcommand (param) + CAP_CLEAR = "CLEAR" // Subcommand (param) + CAP_END = "END" // Subcommand (param) + + AUTHENTICATE = "AUTHENTICATE" +) + +// Numeric IRC replies extracted from the IRCv3 spec. +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" +) + +// 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" +) + +// Other constants +const ( + ERR_TOOMANYMATCHES = "416" // Used on IRCNet + RPL_TOPICWHOTIME = "333" // From ircu, in use on Freenode + RPL_LOCALUSERS = "265" // From aircd, Hybrid, Hybrid, Bahamut, in use on Freenode + RPL_GLOBALUSERS = "266" // From aircd, Hybrid, Hybrid, Bahamut, in use on Freenode +) diff --git a/vendor/gopkg.in/sorcix/irc.v2/doc.go b/vendor/gopkg.in/sorcix/irc.v2/doc.go new file mode 100644 index 0000000..a354664 --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/doc.go @@ -0,0 +1,36 @@ +// Copyright 2014 Vic Demuzere +// +// Use of this source code is governed by the MIT license. + +// Package irc allows your application to speak the IRC protocol. +// +// The Message and Prefix structs provide translation to and from raw IRC messages: +// +// // Parse the IRC-encoded data and store the result in a new struct: +// message := irc.ParseMessage(raw) +// +// // Translate back to a raw IRC message string: +// raw = message.String() +// +// Decoder and Encoder can be used to decode and encode messages in a stream: +// +// // Create a decoder that reads from given io.Reader +// dec := irc.NewDecoder(reader) +// +// // Decode the next IRC message +// message, err := dec.Decode() +// +// // Create an encoder that writes to given io.Writer +// enc := irc.NewEncoder(writer) +// +// // Send a message to the writer. +// enc.Encode(message) +// +// The Conn type combines an Encoder and Decoder for a duplex connection. +// +// c, err := irc.Dial("irc.server.net:6667") +// +// // Methods from both Encoder and Decoder are available +// message, err := c.Decode() +// +package irc // import "gopkg.in/sorcix/irc.v2" diff --git a/vendor/gopkg.in/sorcix/irc.v2/internal/strings.go b/vendor/gopkg.in/sorcix/irc.v2/internal/strings.go new file mode 100644 index 0000000..0021193 --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/internal/strings.go @@ -0,0 +1,19 @@ +// Copyright 2014 Vic Demuzere +// +// Use of this source code is governed by the MIT license. + +// +build go1.2 + +// Documented in strings_legacy.go + +package internal + +import ( + "strings" +) + +// IndexByte is a compatibility function so strings.IndexByte can be used in +// older versions of go. +func IndexByte(s string, c byte) int { + return strings.IndexByte(s, c) +} diff --git a/vendor/gopkg.in/sorcix/irc.v2/internal/strings_legacy.go b/vendor/gopkg.in/sorcix/irc.v2/internal/strings_legacy.go new file mode 100644 index 0000000..5cf8979 --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/internal/strings_legacy.go @@ -0,0 +1,22 @@ +// Copyright 2014 Vic Demuzere +// +// Use of this source code is governed by the MIT license. + +// +build !go1.2 + +// Debian Wheezy only ships Go 1.0: +// https://github.com/sorcix/irc/issues/4 +// +// This code may be removed when Wheezy is no longer supported. + +package internal + +// IndexByte implements strings.IndexByte for Go versions < 1.2. +func IndexByte(s string, c byte) int { + for i := range s { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/vendor/gopkg.in/sorcix/irc.v2/message.go b/vendor/gopkg.in/sorcix/irc.v2/message.go new file mode 100644 index 0000000..628c705 --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/message.go @@ -0,0 +1,309 @@ +// Copyright 2014 Vic Demuzere +// +// Use of this source code is governed by the MIT license. + +package irc + +import ( + "bytes" + "strings" + + "gopkg.in/sorcix/irc.v2/internal" +) + +// Various constants used for formatting IRC messages. +const ( + prefix byte = 0x3A // Prefix or last argument + prefixUser byte = 0x21 // Username + prefixHost byte = 0x40 // Hostname + space byte = 0x20 // Separator + + maxLength = 510 // Maximum length is 512 - 2 for the line endings. +) + +func cutsetFunc(r rune) bool { + // Characters to trim from prefixes/messages. + return r == '\r' || r == '\n' +} + +// Sender represents objects that are able to send messages to an IRC server. +// +// As there might be a message queue, it is possible that Send returns a nil +// error, but the message is not sent (yet). The error value is only used when +// it is certain that sending the message is impossible. +// +// This interface is not used inside this package, and shouldn't have been +// defined here in the first place. For backwards compatibility only. +type Sender interface { + Send(*Message) error +} + +// Prefix represents the prefix (sender) of an IRC message. +// See RFC1459 section 2.3.1. +// +// | [ '!' ] [ '@' ] +// +type Prefix struct { + Name string // Nick- or servername + User string // Username + Host string // Hostname +} + +// ParsePrefix takes a string and attempts to create a Prefix struct. +func ParsePrefix(raw string) (p *Prefix) { + + p = new(Prefix) + + user := internal.IndexByte(raw, prefixUser) + host := internal.IndexByte(raw, prefixHost) + + switch { + + case user > 0 && host > user: + p.Name = raw[:user] + p.User = raw[user+1 : host] + p.Host = raw[host+1:] + + case user > 0: + p.Name = raw[:user] + p.User = raw[user+1:] + + case host > 0: + p.Name = raw[:host] + p.Host = raw[host+1:] + + default: + p.Name = raw + + } + + return p +} + +// Len calculates the length of the string representation of this prefix. +func (p *Prefix) Len() (length int) { + length = len(p.Name) + if len(p.User) > 0 { + length = length + len(p.User) + 1 + } + if len(p.Host) > 0 { + length = length + len(p.Host) + 1 + } + return +} + +// Bytes returns a []byte representation of this prefix. +func (p *Prefix) Bytes() []byte { + buffer := new(bytes.Buffer) + p.writeTo(buffer) + return buffer.Bytes() +} + +// String returns a string representation of this prefix. +func (p *Prefix) String() (s string) { + // Benchmarks revealed that in this case simple string concatenation + // is actually faster than using a ByteBuffer as in (*Message).String() + s = p.Name + if len(p.User) > 0 { + s = s + string(prefixUser) + p.User + } + if len(p.Host) > 0 { + s = s + string(prefixHost) + p.Host + } + return +} + +// IsHostmask returns true if this prefix looks like a user hostmask. +func (p *Prefix) IsHostmask() bool { + return len(p.User) > 0 && len(p.Host) > 0 +} + +// IsServer returns true if this prefix looks like a server name. +func (p *Prefix) IsServer() bool { + return len(p.User) <= 0 && len(p.Host) <= 0 // && internal.IndexByte(p.Name, '.') > 0 +} + +// writeTo is an utility function to write the prefix to the bytes.Buffer in Message.String(). +func (p *Prefix) writeTo(buffer *bytes.Buffer) { + buffer.WriteString(p.Name) + if len(p.User) > 0 { + buffer.WriteByte(prefixUser) + buffer.WriteString(p.User) + } + if len(p.Host) > 0 { + buffer.WriteByte(prefixHost) + buffer.WriteString(p.Host) + } + return +} + +// Message represents an IRC protocol message. +// See RFC1459 section 2.3.1. +// +// ::= [':' ] +// ::= | [ '!' ] [ '@' ] +// ::= { } | +// ::= ' ' { ' ' } +// ::= [ ':' | ] +// +// ::= +// ::= +// +// ::= CR LF +type Message struct { + *Prefix + Command string + Params []string +} + +func (m *Message) Trailing() string { + if len(m.Params) > 0 { + return m.Params[len(m.Params)-1] + } + return "" +} + +// ParseMessage takes a string and attempts to create a Message struct. +// Returns nil if the Message is invalid. +func ParseMessage(raw string) (m *Message) { + + // Ignore empty messages. + if raw = strings.TrimFunc(raw, cutsetFunc); len(raw) < 2 { + return nil + } + + i, j := 0, 0 + + m = new(Message) + + if raw[0] == prefix { + + // Prefix ends with a space. + i = internal.IndexByte(raw, space) + + // Prefix string must not be empty if the indicator is present. + if i < 2 { + return nil + } + + m.Prefix = ParsePrefix(raw[1:i]) + + // Skip space at the end of the prefix + i++ + } + + // Find end of command + j = i + internal.IndexByte(raw[i:], space) + + // Extract command + if j > i { + m.Command = strings.ToUpper(raw[i:j]) + } else { + m.Command = strings.ToUpper(raw[i:]) + + // We're done here! + return m + } + + // Find prefix for trailer. Note that because we need to match the trailing + // argument even if it's the only one, we can't skip the space until we've + // searched for it. + i = strings.Index(raw[j:], " :") + + // Skip the space + j++ + + if i < 0 { + + // There is no trailing argument! + m.Params = strings.Split(raw[j:], string(space)) + + // We're done here! + return m + } + + // Compensate for index on substring. Note that we skipped the space after + // looking for i, so we need to subtract 1 to account for that. + i = i + j - 1 + + // Check if we need to parse arguments. + if i > j { + m.Params = strings.Split(raw[j:i], string(space)) + } + + m.Params = append(m.Params, raw[i+2:]) + + return m +} + +// Len calculates the length of the string representation of this message. +func (m *Message) Len() (length int) { + + if m.Prefix != nil { + length = m.Prefix.Len() + 2 // Include prefix and trailing space + } + + length = length + len(m.Command) + + if len(m.Params) > 0 { + length = length + len(m.Params) + for _, param := range m.Params { + length = length + len(param) + } + + // Add one for the colon in the trailing parameter + length++ + } + + return +} + +// Bytes returns a []byte representation of this message. +// +// As noted in rfc2812 section 2.3, messages should not exceed 512 characters +// in length. This method forces that limit by discarding any characters +// exceeding the length limit. +func (m *Message) Bytes() []byte { + + buffer := new(bytes.Buffer) + + // Message prefix + if m.Prefix != nil { + buffer.WriteByte(prefix) + m.Prefix.writeTo(buffer) + buffer.WriteByte(space) + } + + // Command is required + buffer.WriteString(m.Command) + + // Space separated list of arguments + if len(m.Params) > 1 { + buffer.WriteByte(space) + buffer.WriteString(strings.Join(m.Params[:len(m.Params)-1], string(space))) + } + + if len(m.Params) > 0 { + buffer.WriteByte(space) + buffer.WriteByte(prefix) + buffer.WriteString(m.Trailing()) + } + + // We need the limit the buffer length. + if buffer.Len() > (maxLength) { + buffer.Truncate(maxLength) + } + + return buffer.Bytes() +} + +// String returns a string representation of this message. +// +// As noted in rfc2812 section 2.3, messages should not exceed 512 characters +// in length. This method forces that limit by discarding any characters +// exceeding the length limit. +func (m *Message) String() string { + return string(m.Bytes()) +} diff --git a/vendor/gopkg.in/sorcix/irc.v2/stream.go b/vendor/gopkg.in/sorcix/irc.v2/stream.go new file mode 100644 index 0000000..c4af9af --- /dev/null +++ b/vendor/gopkg.in/sorcix/irc.v2/stream.go @@ -0,0 +1,134 @@ +// Copyright 2014 Vic Demuzere +// +// Use of this source code is governed by the MIT license. + +package irc + +import ( + "bufio" + "io" + "net" + "sync" +) + +// Messages are delimited with CR and LF line endings, +// we're using the last one to split the stream. Both are removed +// during message parsing. +const delim byte = '\n' + +var endline = []byte("\r\n") + +// A Conn represents an IRC network protocol connection. +// It consists of an Encoder and Decoder to manage I/O. +type Conn struct { + Encoder + Decoder + + conn io.ReadWriteCloser +} + +// NewConn returns a new Conn using rwc for I/O. +func NewConn(rwc io.ReadWriteCloser) *Conn { + return &Conn{ + Encoder: Encoder{ + writer: rwc, + }, + Decoder: Decoder{ + reader: bufio.NewReader(rwc), + }, + conn: rwc, + } +} + +// Dial connects to the given address using net.Dial and +// then returns a new Conn for the connection. +func Dial(addr string) (*Conn, error) { + c, err := net.Dial("tcp", addr) + + if err != nil { + return nil, err + } + + return NewConn(c), nil +} + +// Close closes the underlying ReadWriteCloser. +func (c *Conn) Close() error { + return c.conn.Close() +} + +// A Decoder reads Message objects from an input stream. +type Decoder struct { + reader *bufio.Reader + line string + mu sync.Mutex +} + +// NewDecoder returns a new Decoder that reads from r. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + reader: bufio.NewReader(r), + } +} + +// Decode attempts to read a single Message from the stream. +// +// Returns a non-nil error if the read failed. +func (dec *Decoder) Decode() (m *Message, err error) { + + dec.mu.Lock() + dec.line, err = dec.reader.ReadString(delim) + dec.mu.Unlock() + + if err != nil { + return nil, err + } + + return ParseMessage(dec.line), nil +} + +// An Encoder writes Message objects to an output stream. +type Encoder struct { + writer io.Writer + mu sync.Mutex +} + +// NewEncoder returns a new Encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + writer: w, + } +} + +// Encode writes the IRC encoding of m to the stream. +// +// This method may be used from multiple goroutines. +// +// Returns an non-nil error if the write to the underlying stream stopped early. +func (enc *Encoder) Encode(m *Message) (err error) { + + _, err = enc.Write(m.Bytes()) + + return +} + +// Write writes len(p) bytes from p followed by CR+LF. +// +// This method can be used simultaneously from multiple goroutines, +// it guarantees to serialize access. However, writing a single IRC message +// using multiple Write calls will cause corruption. +func (enc *Encoder) Write(p []byte) (n int, err error) { + + enc.mu.Lock() + n, err = enc.writer.Write(p) + + if err != nil { + enc.mu.Unlock() + return + } + + _, err = enc.writer.Write(endline) + enc.mu.Unlock() + + return +} diff --git a/vendor/vendor.json b/vendor/vendor.json new file mode 100644 index 0000000..ff3ee4b --- /dev/null +++ b/vendor/vendor.json @@ -0,0 +1,19 @@ +{ + "comment": "", + "ignore": "test", + "package": [ + { + "checksumSHA1": "mRNv6NTAfGphwvF7YzNk8XOhX10=", + "path": "gopkg.in/sorcix/irc.v2", + "revision": "63b1858457fa9439c5f37124100605a0b0d98bd8", + "revisionTime": "2016-05-13T12:23:15Z" + }, + { + "checksumSHA1": "745d6gaBv5Ni3YrPvslpM4BoU6A=", + "path": "gopkg.in/sorcix/irc.v2/internal", + "revision": "63b1858457fa9439c5f37124100605a0b0d98bd8", + "revisionTime": "2016-05-13T12:23:15Z" + } + ], + "rootPath": "anonircd" +}