PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/client.go

https://github.com/dominikh/go-irc
Go | 823 lines | 665 code | 96 blank | 62 comment | 149 complexity | d2a2c3e2cad1983f3fc86ec3c34e01f2 MD5 | raw file
  1. package irc // import "honnef.co/go/irc"
  2. import (
  3. "bufio"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net"
  9. "runtime"
  10. "strings"
  11. "sync"
  12. "time"
  13. "unicode"
  14. "unicode/utf8"
  15. )
  16. const CTCPDelim = "\001"
  17. type Logger interface {
  18. Incoming(*Message)
  19. Outgoing(*Message)
  20. Info(...interface{})
  21. Debug(...interface{})
  22. Panic(interface{})
  23. }
  24. // RawLogger only logs incoming and outgoing messages in their raw
  25. // form. To differentiate incoming from outgoing messages, it prefixes
  26. // incoming messages with -> and outgoing messages with <-, in
  27. // addition to a timestamp.
  28. type RawLogger struct {
  29. mu sync.Mutex
  30. W io.Writer
  31. }
  32. func (l *RawLogger) Incoming(m *Message) {
  33. l.mu.Lock()
  34. defer l.mu.Unlock()
  35. fmt.Fprintf(l.W, "%s -> %s\n", time.Now().Format(time.RFC3339), m.Raw)
  36. }
  37. func (l *RawLogger) Outgoing(m *Message) {
  38. l.mu.Lock()
  39. defer l.mu.Unlock()
  40. fmt.Fprintf(l.W, "%s <- %s\n", time.Now().Format(time.RFC3339), m.Raw)
  41. }
  42. func (l *RawLogger) Info(...interface{}) {}
  43. func (l *RawLogger) Debug(...interface{}) {}
  44. func (l *RawLogger) Panic(interface{}) {}
  45. // FormattedLogger is a generic logger that supports all types of log
  46. // messages and prefixes them with tags as well as timestamps.
  47. //
  48. // Example output:
  49. // 2009-11-10T23:00:00Z [INC ] Incoming message
  50. // 2009-11-10T23:00:01Z [OUT ] Outgoing message
  51. // 2009-11-10T23:00:02Z [INFO ] Info message
  52. // 2009-11-10T23:00:03Z [DEBUG] Debug messages
  53. // 2009-11-10T23:00:04Z [PANIC] Panic message
  54. // 2009-11-10T23:00:04Z [PANIC] Stacktrace line 1
  55. // ...
  56. type FormattedLogger struct {
  57. mu sync.Mutex
  58. W io.Writer
  59. }
  60. func (l *FormattedLogger) Incoming(m *Message) {
  61. l.mu.Lock()
  62. defer l.mu.Unlock()
  63. fmt.Fprintf(l.W, "%s [INC ] %s\n", time.Now().Format(time.RFC3339), m.Raw)
  64. }
  65. func (l *FormattedLogger) Outgoing(m *Message) {
  66. l.mu.Lock()
  67. defer l.mu.Unlock()
  68. fmt.Fprintf(l.W, "%s [OUT ] %s\n", time.Now().Format(time.RFC3339), m.Raw)
  69. }
  70. func (l *FormattedLogger) Info(args ...interface{}) {
  71. l.mu.Lock()
  72. defer l.mu.Unlock()
  73. fmt.Fprintf(l.W, "%s [INFO ] ", time.Now().Format(time.RFC3339))
  74. fmt.Fprintln(l.W, args...)
  75. }
  76. func (l *FormattedLogger) Debug(args ...interface{}) {
  77. l.mu.Lock()
  78. defer l.mu.Unlock()
  79. fmt.Fprintf(l.W, "%s [DEBUG] ", time.Now().Format(time.RFC3339))
  80. fmt.Fprintln(l.W, args...)
  81. }
  82. func (l *FormattedLogger) Panic(arg interface{}) {
  83. l.mu.Lock()
  84. defer l.mu.Unlock()
  85. fmt.Fprintf(l.W, "%s [PANIC] ", time.Now().Format(time.RFC3339))
  86. fmt.Fprintln(l.W, arg)
  87. buf := make([]byte, 64<<10)
  88. n := runtime.Stack(buf, false)
  89. s := string(buf[:n])
  90. lines := strings.Split(s, "\n")
  91. for _, line := range lines {
  92. fmt.Fprintf(l.W, "%s [PANIC] %s\n", time.Now().Format(time.RFC3339), line)
  93. }
  94. }
  95. // MultiLogger allows using multiple log targets at once. All log
  96. // messages get sent to all loggers.
  97. type MultiLogger struct {
  98. Loggers []Logger
  99. }
  100. func (l *MultiLogger) Incoming(m *Message) {
  101. for _, ll := range l.Loggers {
  102. ll.Incoming(m)
  103. }
  104. }
  105. func (l *MultiLogger) Outgoing(m *Message) {
  106. for _, ll := range l.Loggers {
  107. ll.Outgoing(m)
  108. }
  109. }
  110. func (l *MultiLogger) Info(args ...interface{}) {
  111. for _, ll := range l.Loggers {
  112. ll.Info(args...)
  113. }
  114. }
  115. func (l *MultiLogger) Debug(args ...interface{}) {
  116. for _, ll := range l.Loggers {
  117. ll.Debug(args...)
  118. }
  119. }
  120. func (l *MultiLogger) Panic(arg interface{}) {
  121. for _, ll := range l.Loggers {
  122. ll.Panic(arg)
  123. }
  124. }
  125. type nullLogger struct{}
  126. func (nullLogger) Incoming(*Message) {}
  127. func (nullLogger) Outgoing(*Message) {}
  128. func (nullLogger) Info(...interface{}) {}
  129. func (nullLogger) Debug(...interface{}) {}
  130. func (nullLogger) Panic(interface{}) {}
  131. var _ Logger = (*RawLogger)(nil)
  132. var _ Logger = (*FormattedLogger)(nil)
  133. var _ Logger = (*MultiLogger)(nil)
  134. var _ Logger = (*nullLogger)(nil)
  135. type Mask struct {
  136. Nick string
  137. User string
  138. Host string
  139. }
  140. type Message struct {
  141. // The raw IRC message
  142. Raw string
  143. Prefix Mask
  144. Command string
  145. Params []string
  146. // The signal/command name to use for routing. In most cases, this
  147. // will equal the command. In some cases, such as CTCP messages,
  148. // it will be different.
  149. Signal string
  150. }
  151. // Copy performs a deep copy of a message. This is useful when passing
  152. // messages to functions that should have ownership over the message,
  153. // including the slice of parameters. Usually this will be used when
  154. // implementing muxers.
  155. func (m *Message) Copy() *Message {
  156. m2 := *m
  157. m2.Params = make([]string, len(m.Params))
  158. copy(m2.Params, m.Params)
  159. return &m2
  160. }
  161. func pad(in []string, n int) []string {
  162. if len(in) == n {
  163. return in
  164. }
  165. out := make([]string, n)
  166. copy(out, in)
  167. return out
  168. }
  169. // Parse parses an IRC message as it may be sent or received.
  170. func Parse(s string) *Message {
  171. m := &Message{Raw: s}
  172. if s[0] == ':' {
  173. parts := pad(strings.SplitN(s, " ", 3), 3)
  174. prefix := parts[0][1:]
  175. if strings.Index(prefix, "!") == -1 {
  176. m.Prefix.Host = prefix
  177. } else {
  178. parts := strings.FieldsFunc(prefix, func(r rune) bool { return r == '!' || r == '@' })
  179. parts = pad(parts, 3)
  180. m.Prefix.Nick = parts[0]
  181. m.Prefix.User = parts[1]
  182. m.Prefix.Host = parts[2]
  183. }
  184. m.Command = parts[1]
  185. m.Signal = m.Command
  186. m.Params = parseParams(parts[2])
  187. return m
  188. }
  189. parts := pad(strings.SplitN(s, " ", 2), 2)
  190. m.Command = parts[0]
  191. m.Signal = m.Command
  192. m.Params = parseParams(parts[1])
  193. return m
  194. }
  195. func parseParams(params string) []string {
  196. if len(params) == 0 {
  197. return nil
  198. }
  199. if params[0] == ':' {
  200. if len(params) == 1 {
  201. return []string{""}
  202. }
  203. return []string{strings.TrimRight(params[1:], " ")}
  204. }
  205. idx := strings.Index(params, " :")
  206. if idx == -1 {
  207. return strings.Split(params, " ")
  208. }
  209. left, right := params[:idx], strings.TrimRight(params[idx+2:], " ")
  210. var out []string
  211. if len(left) > 0 {
  212. out = strings.Split(left, " ")
  213. }
  214. if idx < len(params) {
  215. out = append(out, right)
  216. }
  217. return out
  218. }
  219. func (m *Message) String() string {
  220. return m.Raw
  221. }
  222. // IsNumeric reports whether the message's command is numeric (e.g.
  223. // 001) as opposed to a string (e.g. "JOIN".)
  224. func (m *Message) IsNumeric() bool {
  225. if len(m.Command) != 3 {
  226. return false
  227. }
  228. s := m.Command
  229. return s[0] >= '0' && s[0] <= '9' &&
  230. s[1] >= '0' && s[1] <= '9' &&
  231. s[2] >= '0' && s[2] <= '9'
  232. }
  233. // IsError reports whether the message's command denotes an error,
  234. // i.e. whether it is numeric and the number code starts with either a
  235. // 4 or a 5.
  236. func (m *Message) IsError() bool {
  237. return m.IsNumeric() && (m.Command[0] == '4' || m.Command[0] == '5')
  238. }
  239. func (m *Message) IsCTCP() bool {
  240. if len(m.Params) == 0 {
  241. return false
  242. }
  243. s := m.Params[len(m.Params)-1]
  244. if len(s) < 2 {
  245. return false
  246. }
  247. return s[0] == 1 && s[len(s)-1] == 1
  248. }
  249. func (m *Message) CTCP() (*CTCPMessage, error) {
  250. if !m.IsCTCP() {
  251. return nil, errors.New("not a CTCP message")
  252. }
  253. return ParseCTCP(m.Params[len(m.Params)-1])
  254. }
  255. type CTCPMessage struct {
  256. Raw string
  257. Command string
  258. Params []string
  259. }
  260. func ParseCTCP(s string) (*CTCPMessage, error) {
  261. if len(s) < 2 {
  262. return nil, errors.New("not a CTCP message")
  263. }
  264. s = s[1 : len(s)-1]
  265. m := &CTCPMessage{Raw: s}
  266. parts := strings.Split(s, " ")
  267. m.Command = parts[0]
  268. if len(parts) > 1 {
  269. m.Params = parts[1:]
  270. }
  271. return m, nil
  272. }
  273. type Handler interface {
  274. Process(*Client, *Message)
  275. }
  276. type HandlerFunc func(*Client, *Message)
  277. func (f HandlerFunc) Process(c *Client, m *Message) {
  278. f(c, m)
  279. }
  280. type Mux struct {
  281. mu *sync.RWMutex
  282. m map[string][]Handler
  283. }
  284. func NewMux() *Mux {
  285. mux := &Mux{new(sync.RWMutex), make(map[string][]Handler)}
  286. return mux
  287. }
  288. func (mux *Mux) Handle(signal string, handler Handler) {
  289. mux.mu.Lock()
  290. defer mux.mu.Unlock()
  291. mux.m[signal] = append(mux.m[signal], handler)
  292. }
  293. func (mux *Mux) HandleFunc(signal string, handler func(*Client, *Message)) {
  294. mux.Handle(signal, HandlerFunc(handler))
  295. }
  296. func (mux *Mux) Handlers(m *Message) (hs []Handler) {
  297. mux.mu.RLock()
  298. defer mux.mu.RUnlock()
  299. hs = mux.m[m.Signal]
  300. hs = append(hs, mux.m[""]...)
  301. return hs
  302. }
  303. func (mux *Mux) Process(c *Client, m *Message) {
  304. hs := mux.Handlers(m)
  305. if hs != nil {
  306. for _, h := range hs {
  307. go h.Process(c, m.Copy())
  308. }
  309. }
  310. }
  311. var DefaultMux = NewMux()
  312. func Handle(signal string, handler Handler) { DefaultMux.Handle(signal, handler) }
  313. func HandleFunc(signal string, handler func(*Client, *Message)) {
  314. DefaultMux.HandleFunc(signal, handler)
  315. }
  316. type Authenticator interface {
  317. Authenticate(c *Client)
  318. }
  319. type Muxer interface {
  320. Handler
  321. Handle(command string, handler Handler)
  322. HandleFunc(command string, handler func(*Client, *Message))
  323. Handlers(m *Message) (hs []Handler)
  324. }
  325. type Client struct {
  326. Authenticator Authenticator
  327. Err error
  328. // TODO proper documentation. The ISupport field will be
  329. // automatically set to a default value during dialing and will
  330. // then be populated by the IRC server.
  331. ISupport *ISupport
  332. Logger Logger
  333. Mux Muxer
  334. Name string
  335. Nick string
  336. Password string
  337. TLSConfig *tls.Config
  338. User string
  339. mu sync.RWMutex
  340. currentNick string
  341. connected []string
  342. conn net.Conn
  343. chSend chan sendMessage
  344. chQuit chan struct{}
  345. scanner *bufio.Scanner
  346. dead bool
  347. }
  348. type sendMessage struct {
  349. msg string
  350. ch chan error
  351. }
  352. func inStrings(in []string, s string) bool {
  353. for _, e := range in {
  354. if e == s {
  355. return true
  356. }
  357. }
  358. return false
  359. }
  360. func (c *Client) Connected() bool {
  361. c.mu.RLock()
  362. defer c.mu.RUnlock()
  363. return inStrings(c.connected, ERR_NOMOTD) ||
  364. (inStrings(c.connected, RPL_WELCOME) &&
  365. inStrings(c.connected, RPL_YOURHOST) &&
  366. inStrings(c.connected, RPL_CREATED) &&
  367. inStrings(c.connected, RPL_MYINFO))
  368. }
  369. var ErrDeadClient = errors.New("dead client")
  370. func (c *Client) Dial(network, addr string) error {
  371. c.mu.Lock()
  372. if c.dead {
  373. return ErrDeadClient
  374. }
  375. c.mu.Unlock()
  376. conn, err := net.Dial(network, addr)
  377. if err != nil {
  378. return err
  379. }
  380. c.conn = conn
  381. c.init()
  382. return nil
  383. }
  384. func (c *Client) DialTLS(network, addr string) error {
  385. c.mu.Lock()
  386. if c.dead {
  387. return ErrDeadClient
  388. }
  389. c.mu.Unlock()
  390. conn, err := tls.Dial(network, addr, c.TLSConfig)
  391. if err != nil {
  392. return err
  393. }
  394. c.conn = conn
  395. c.init()
  396. return nil
  397. }
  398. func (c *Client) init() {
  399. c.mu.Lock()
  400. defer c.mu.Unlock()
  401. if c.Mux == nil {
  402. c.Mux = DefaultMux
  403. }
  404. if c.Logger == nil {
  405. c.Logger = nullLogger{}
  406. }
  407. c.ISupport = NewISupport()
  408. c.chSend = make(chan sendMessage)
  409. c.chQuit = make(chan struct{})
  410. c.scanner = bufio.NewScanner(c.conn)
  411. c.connected = nil
  412. c.currentNick = ""
  413. go c.writeLoop()
  414. }
  415. func (c *Client) error(err error) {
  416. c.mu.Lock()
  417. defer c.mu.Unlock()
  418. if c.Err != nil {
  419. return
  420. }
  421. c.Err = err
  422. c.dead = true
  423. c.conn.Close()
  424. close(c.chQuit)
  425. }
  426. func (c *Client) Process() error {
  427. go c.pingLoop()
  428. if c.Authenticator != nil {
  429. go c.Authenticator.Authenticate(c)
  430. } else {
  431. go c.Login()
  432. }
  433. return c.readLoop()
  434. }
  435. type readReply struct {
  436. msg *Message
  437. err error
  438. }
  439. func (c *Client) read(ch chan readReply) {
  440. ok := c.scanner.Scan()
  441. if !ok {
  442. err := c.scanner.Err()
  443. if err == nil {
  444. err = io.EOF
  445. }
  446. c.error(err)
  447. return
  448. }
  449. c.conn.SetReadDeadline(time.Now().Add(240 * time.Second))
  450. m := Parse(c.scanner.Text())
  451. ch <- readReply{m, nil}
  452. }
  453. func (c *Client) Read() (*Message, error) {
  454. select {
  455. case <-c.chQuit:
  456. return nil, c.Err
  457. default:
  458. }
  459. ch := make(chan readReply, 1)
  460. go c.read(ch)
  461. select {
  462. case reply := <-ch:
  463. m := reply.msg
  464. c.Logger.Incoming(m)
  465. switch m.Command {
  466. case "PING":
  467. c.Sendf("PONG %s", reply.msg.Params[0])
  468. case RPL_ISUPPORT:
  469. c.ISupport.Parse(m)
  470. case RPL_WELCOME, RPL_YOURHOST, RPL_CREATED, RPL_MYINFO, ERR_NOMOTD:
  471. c.mu.Lock()
  472. c.connected = append(c.connected, m.Command)
  473. c.currentNick = m.Params[0]
  474. c.mu.Unlock()
  475. case "NICK":
  476. // We don't need to lock for reading here, there is no
  477. // concurrent writer to c.currentNick
  478. if m.Prefix.Nick != c.currentNick {
  479. break
  480. }
  481. c.mu.Lock()
  482. c.currentNick = m.Params[0]
  483. c.mu.Unlock()
  484. }
  485. return reply.msg, reply.err
  486. case <-c.chQuit:
  487. return nil, c.Err
  488. }
  489. }
  490. func (c *Client) pingLoop() {
  491. ticker := time.NewTicker(120 * time.Second)
  492. for {
  493. select {
  494. case <-ticker.C:
  495. c.Send("PING :0")
  496. case <-c.chQuit:
  497. return
  498. }
  499. }
  500. }
  501. func (c *Client) readLoop() error {
  502. for {
  503. m, err := c.Read()
  504. if err != nil {
  505. return err
  506. }
  507. switch m.Command {
  508. case RPL_WELCOME, RPL_YOURHOST, RPL_CREATED, RPL_MYINFO, ERR_NOMOTD:
  509. if c.Connected() {
  510. c.Mux.Process(c, &Message{Signal: "irc:connected"})
  511. }
  512. case "PRIVMSG", "NOTICE":
  513. if ctcp, err := m.CTCP(); err == nil {
  514. m := m.Copy()
  515. m.Signal = "ctcp:" + ctcp.Command
  516. c.Mux.Process(c, m)
  517. }
  518. }
  519. c.Mux.Process(c, m)
  520. }
  521. }
  522. func (c *Client) writeLoop() {
  523. for {
  524. select {
  525. case m := <-c.chSend:
  526. s := m.msg
  527. c.Logger.Outgoing(Parse(s))
  528. c.conn.SetWriteDeadline(time.Now().Add(240 * time.Second))
  529. _, err := io.WriteString(c.conn, s+"\r\n")
  530. if err != nil {
  531. m.ch <- err
  532. c.error(err)
  533. }
  534. m.ch <- nil
  535. case <-c.chQuit:
  536. return
  537. }
  538. }
  539. }
  540. func firstError(errs ...error) error {
  541. for _, err := range errs {
  542. if err != nil {
  543. return err
  544. }
  545. }
  546. return nil
  547. }
  548. func (c *Client) Login() error {
  549. var err1, err2, err3 error
  550. if len(c.Password) > 0 {
  551. err1 = c.Sendf("PASS %s", c.Password)
  552. }
  553. err2 = c.Sendf("USER %s 0 * :%s", c.User, c.Name)
  554. err3 = c.Sendf("NICK %s", c.Nick)
  555. return firstError(err1, err2, err3)
  556. }
  557. func (c *Client) Send(s string) error {
  558. ch := make(chan error)
  559. select {
  560. case c.chSend <- sendMessage{s, ch}:
  561. return <-ch
  562. case <-c.chQuit:
  563. return ErrDeadClient
  564. }
  565. }
  566. func (c *Client) Sendf(format string, args ...interface{}) error {
  567. return c.Send(fmt.Sprintf(format, args...))
  568. }
  569. // Privmsg sends a PRIVMSG message to target.
  570. func (c *Client) Privmsg(target, message string) error {
  571. return c.Sendf("PRIVMSG %s :%s", target, message)
  572. }
  573. // PrivmsgSplit sends a PRIVMSG message to target and splits it into
  574. // chunks of n. See SplitMessage for more information on how said
  575. // splitting is done.
  576. func (c *Client) PrivmsgSplit(target, message string, n int) error {
  577. s := fmt.Sprintf("PRIVMSG %s :%s", target, message)
  578. for _, msg := range SplitMessage(s, n) {
  579. err := c.Send(msg)
  580. if err != nil {
  581. return err
  582. }
  583. }
  584. return nil
  585. }
  586. // Notice sends a NOTICE message to target.
  587. func (c *Client) Notice(target, message string) error {
  588. return c.Sendf("NOTICE %s :%s", target, message)
  589. }
  590. // NoticeSplit sends a NOTICE message to target and splits it into
  591. // chunks of n. See SplitMessage for more information on how said
  592. // splitting is done.
  593. func (c *Client) NoticeSplit(target, message string, n int) error {
  594. s := fmt.Sprintf("NOTICE %s :%s", target, message)
  595. for _, msg := range SplitMessage(s, n) {
  596. err := c.Send(msg)
  597. if err != nil {
  598. return err
  599. }
  600. }
  601. return nil
  602. }
  603. func (c *Client) Reply(m *Message, response string) error {
  604. if m.Command != "PRIVMSG" && m.Command != "NOTICE" {
  605. panic("cannot reply to " + m.Command)
  606. }
  607. target, ok := c.ChannelForMsg(m)
  608. if !ok {
  609. // TODO message was sent to us directly, not a channel
  610. target = m.Prefix.Nick
  611. }
  612. return c.Privmsg(target, response)
  613. }
  614. func (c *Client) ReplySplit(m *Message, response string, n int) error {
  615. if m.Command != "PRIVMSG" && m.Command != "NOTICE" {
  616. panic("cannot reply to " + m.Command)
  617. }
  618. target, ok := c.ChannelForMsg(m)
  619. if !ok {
  620. // message was sent to us directly, not a channel
  621. target = m.Prefix.Nick
  622. }
  623. return c.PrivmsgSplit(target, response, n)
  624. }
  625. func (c *Client) ReplyCTCP(m *Message, response string) error {
  626. if !m.IsCTCP() {
  627. panic("message is not a CTCP")
  628. }
  629. ctcp, _ := m.CTCP()
  630. return c.Notice(m.Prefix.Nick, fmt.Sprintf("%s%s %s%s", CTCPDelim, ctcp.Command, response, CTCPDelim))
  631. }
  632. func inRunes(runes []rune, search rune) bool {
  633. for _, rune := range runes {
  634. if rune == search {
  635. return true
  636. }
  637. }
  638. return false
  639. }
  640. func (c *Client) ChannelForMsg(m *Message) (string, bool) {
  641. if len(m.Params) == 0 {
  642. return "", false
  643. }
  644. switch m.Command {
  645. case "INVITE", RPL_CHANNELMODEIS, RPL_BANLIST:
  646. return m.Params[1], true
  647. case RPL_NAMEREPLY:
  648. return m.Params[2], true
  649. default:
  650. if inRunes(c.ISupport.ChanTypes, []rune(m.Params[0])[0]) {
  651. return m.Params[0], true
  652. }
  653. if m.IsNumeric() && len(m.Params) > 1 && inRunes(c.ISupport.ChanTypes, []rune(m.Params[1])[0]) {
  654. return m.Params[1], true
  655. }
  656. }
  657. return "", false
  658. }
  659. // SplitMessage splits a PRIVMSG or NOTICE into many messages, each at
  660. // most n bytes long and repeating the command and target list. Split
  661. // assumes UTF-8 encoding but does not support combining characters.
  662. // It does not split in the middle of words.
  663. //
  664. // IRC messages can be at most 512 bytes long. This includes the
  665. // terminating \r\n as well as the message prefix that the server
  666. // prepends, consisting of a : sign and a hostmask. For optimal
  667. // results, n should be calculated accordingly, but a safe value that
  668. // doesn't require calculations would be around 350.
  669. //
  670. // The result is undefined if n is smaller than the command and target
  671. // portions or if the list of targets is missing. If a single word is
  672. // longer than n bytes, it will be split.
  673. func SplitMessage(s string, n int) []string {
  674. if len(s) < n || !utf8.ValidString(s) {
  675. return []string{s}
  676. }
  677. pl := strings.Index(s, " :") + 2
  678. repeat := s[:pl]
  679. s = s[pl:]
  680. n -= pl
  681. if n <= 0 {
  682. n = 1
  683. }
  684. var parts []string
  685. for len(s) > n {
  686. pos := strings.LastIndex(s[:n], " ")
  687. if pos == -1 {
  688. pos = n
  689. }
  690. dir := -1
  691. for {
  692. if r, size := utf8.DecodeLastRuneInString(s[:pos]); r != utf8.RuneError || size != 1 {
  693. break
  694. }
  695. pos += dir
  696. if pos == 0 {
  697. pos = 1
  698. dir = 1
  699. }
  700. }
  701. parts = append(parts, s[:pos])
  702. s = strings.TrimLeftFunc(s[pos:], unicode.IsSpace)
  703. }
  704. if len(s) > 0 {
  705. parts = append(parts, s)
  706. }
  707. for i := range parts {
  708. parts[i] = repeat + parts[i]
  709. }
  710. return parts
  711. }
  712. func (c *Client) Join(channel, password string) error {
  713. // FIXME do not return until we actually joined the channel. or
  714. // maybe put that in the framework?
  715. if password == "" {
  716. return c.Sendf("JOIN %s", channel)
  717. }
  718. return c.Sendf("JOIN %s %s", channel, password)
  719. }
  720. func (c *Client) SetNick(nick string) error {
  721. return c.Sendf("NICK %s", nick)
  722. }
  723. func (c *Client) CurrentNick() string {
  724. c.mu.RLock()
  725. defer c.mu.RUnlock()
  726. return c.currentNick
  727. }