irc.go (7077B)
1 // Copyright 2013 TLH and dsp. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package irc 6 7 import ( 8 "crypto/tls" 9 "encoding/json" 10 "fmt" 11 "io/ioutil" 12 "log" 13 "net" 14 "strings" 15 "sync" 16 ) 17 18 type Context struct { 19 conn net.Conn // actual connection 20 outgoingMsg chan Message // TX messaging channel 21 incomingMsg chan Message // RX messaging channel 22 ev []Event // slice of callbacks 23 evnum int // number of callbacks 24 network network // irc network state 25 intercepts []*chan Message // intercept chans 26 sync.Mutex 27 } 28 29 type Config struct { 30 NetworkName string // network name 31 Nick string // nickname 32 User string // username 33 RealName string // real name 34 Pass string // password 35 Serv string // server address 36 Port string // server port 37 Ssl bool // enable/disable ssl 38 Ipv6 bool // enable/disable ipv6 (not implemented) 39 Channels []string // array of channels to join 40 } 41 42 type Channel struct { 43 name string // name of irc channel 44 key string // password for irc channel 45 joined bool // whether we have joined this channel or not 46 } 47 48 type network struct { 49 name string // network name 50 nick string // nickname 51 user string // username 52 realName string // real name 53 pass string // password 54 serv string // server address 55 port string // server port 56 tls bool // enable/disable ssl 57 ipv6 bool // enable/disable ipv6 (not implemented) 58 channels []Channel // slice of channels 59 } 60 61 func LoadConfig(fname string) (con *Config, err error) { 62 bytes, err := ioutil.ReadFile(fname) 63 if err != nil { 64 log.Printf(" [LoadConfig] error reading file %s\n", fname) 65 return nil, err 66 } 67 con = new(Config) 68 err = json.Unmarshal(bytes, &con) 69 if err != nil { 70 log.Printf(" [LoadConfig] error reading file %s\n", fname) 71 return nil, err 72 } 73 return con, nil 74 } 75 76 // Create a new IrcContext 77 func NewIrcContext(ircConfig Config) *Context { 78 chans := make([]Channel, len(ircConfig.Channels)) 79 80 for i, _ := range chans { 81 chans[i] = Channel{ircConfig.Channels[i],"",false} 82 } 83 84 network := network{ 85 name: ircConfig.NetworkName, 86 nick: ircConfig.Nick, 87 user: ircConfig.User, 88 realName: ircConfig.RealName, 89 pass: ircConfig.Pass, 90 serv: ircConfig.Serv, 91 port: ircConfig.Port, 92 tls: ircConfig.Ssl, 93 ipv6: ircConfig.Ipv6, 94 channels: chans, 95 } 96 97 return &Context{ 98 outgoingMsg: make(chan Message), 99 incomingMsg: make(chan Message), 100 ev: make([]Event, 0), 101 network: network, 102 intercepts: make([]*chan Message, 0), 103 } 104 } 105 106 func (i *Context) AddIntercept(a *chan Message) { 107 i.Lock() 108 defer i.Unlock() 109 i.intercepts = append(i.intercepts, a) 110 } 111 112 func (i *Context) DelIntercept(ap *chan Message) error { 113 i.Lock() 114 defer i.Unlock() 115 for j := 0; j < len(i.intercepts); j++ { 116 if ap == i.intercepts[j] { 117 if j == 0 { 118 i.intercepts = make([]*chan Message, 0) 119 return nil 120 } 121 i.intercepts = append(i.intercepts[:j], i.intercepts[j+1:]...) 122 return nil 123 } 124 } 125 return fmt.Errorf("Intercept not found") 126 } 127 128 type ChanIter func() (c *Channel, ok bool) 129 130 func MakeChanIter(i *Context) ChanIter { 131 j := 0 132 return func() (c *Channel, ok bool) { 133 i.Lock() 134 defer i.Unlock() 135 if j < len(i.network.channels) { 136 j++ 137 return &i.network.channels[j], true 138 } 139 return nil, false 140 } 141 } 142 143 func (i *Context) JoinChannel(c string, key string) { 144 i.Lock() 145 defer i.Unlock() 146 found := false 147 for _, v := range i.network.channels { 148 if v.name == c { 149 found = true 150 break 151 } 152 } 153 if !found { 154 i.network.channels = append(i.network.channels, 155 Channel{c, key, false}) 156 } 157 for j, v := range i.network.channels { 158 if v.name != c { 159 continue 160 } 161 if !v.joined { 162 i.Join(v.name, v.key) 163 i.network.channels[j].joined = true 164 break 165 } else { 166 i.Join(v.name, v.key) 167 break 168 } 169 } 170 return 171 } 172 173 func (i *Context) JoinChannels() { 174 for _, v := range i.network.channels { 175 i.JoinChannel(v.name, v.key) 176 } 177 } 178 179 func (i *Context) PartChannel(s string, text string) error { 180 i.Lock() 181 defer i.Unlock() 182 for c, v := range i.network.channels { 183 if v.name == s { 184 if v.joined { 185 i.Part(s, text) 186 i.network.channels[c].joined = false 187 return nil 188 } else { 189 return fmt.Errorf("Channel %s is not joined", s) 190 } 191 } 192 } 193 return fmt.Errorf("Can't find channel %s", s) 194 } 195 196 // Connect to the server. Do not join any channels by default. 197 func (i *Context) Connect() error { 198 service := i.network.serv + ":" + i.network.port 199 200 if i.network.tls { 201 if conf, err := loadCerts(); err != nil { 202 return err 203 } else { 204 if conn, err := tls.Dial("tcp", service, conf); err != nil { 205 return err 206 } else { 207 i.conn = conn 208 } 209 } 210 } else { 211 if conn, err := net.Dial("tcp", service); err != nil { 212 return err 213 } else { 214 i.conn = conn 215 } 216 } 217 218 // Fire off the goroutines now! 219 go i.incomingMsgLoop() 220 go i.outgoingMsgLoop() 221 go i.rxRawMessages() 222 223 return nil 224 } 225 226 // Login to server 227 func (i *Context) Login() { 228 if i.network.pass != "" { 229 i.Pass() 230 } 231 i.Nick() 232 i.User() 233 } 234 235 func (i *Context) read(buf []byte) (int, error) { 236 n, err := i.conn.Read(buf) 237 if err != nil { 238 return n, err 239 } 240 return n, nil 241 } 242 243 // This is the actual raw read loop. Here we parse the incoming bytes 244 // and form messages. 245 func (i *Context) rxRawMessages() error { 246 for { 247 var buf [512]byte 248 n, err := i.read(buf[0:]) 249 if err != nil { 250 return err 251 } 252 if n != 0 { 253 log.Println(string(buf[0 : n-1])) 254 s := string(buf[0:n]) 255 256 msg, err := i.ParseRawMessage(s) 257 if err != nil { 258 log.Printf("%s", err) 259 } else { 260 i.incomingMsg <- *msg 261 for _, v := range i.intercepts { 262 *v <- *msg 263 } 264 } 265 } 266 } 267 return nil 268 } 269 270 // Transmit a raw message 271 func (i *Context) TxRawMessage(msg []byte) error { 272 _, err := i.conn.Write(msg[0:]) 273 if err != nil { 274 return err 275 } 276 return nil 277 } 278 279 func (i *Context) String() string { 280 s := fmt.Sprintf("IRC CONTEXT DUMP") 281 if len(i.ev) == 0 { 282 s += fmt.Sprintf("\n\tEvent handlers: [NONE]\n") 283 } else { 284 s += fmt.Sprintf("\n\tEvent handlers: [%d]\n", len(i.ev)) 285 for _, v := range i.ev { 286 s += fmt.Sprintf("\t\tCommand: %s\n", v.Command) 287 } 288 } 289 s += fmt.Sprintf("\tNetwork:\n") 290 s += fmt.Sprintf("\t\tName: %s\n", i.network.name) 291 s += fmt.Sprintf("\t\tNick: %s\n", i.network.nick) 292 s += fmt.Sprintf("\t\tUser: %s\n", i.network.user) 293 if i.network.pass != "" { 294 s += fmt.Sprintf("\t\tPass: %s\n", i.network.pass) 295 } else { 296 s += fmt.Sprintf("\t\tPass: [NONE]\n") 297 } 298 s += fmt.Sprintf("\t\tServer: %s\n", i.network.serv) 299 if i.network.tls { 300 s += fmt.Sprintf("\t\tTLS: on") 301 } else { 302 s += fmt.Sprintf("\t\tTLS: off") 303 } 304 if len(i.network.channels) == 0 { 305 s += fmt.Sprintf("\n\t\tChannels: [NONE]") 306 } else { 307 s += fmt.Sprintf("\n\t\tChannels: [%d]\n", len(i.network.channels)) 308 for _, v := range i.network.channels { 309 s += fmt.Sprintf("\t\t\tName: %s\n", v.name) 310 } 311 } 312 return strings.TrimSpace(s) 313 }