Mirror of github.com/gissleh/irc
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

694 lines
15 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package irc
  2. import (
  3. "bufio"
  4. "context"
  5. "crypto/rand"
  6. "crypto/tls"
  7. "encoding/binary"
  8. "encoding/hex"
  9. "errors"
  10. "fmt"
  11. mathRand "math/rand"
  12. "net"
  13. "strconv"
  14. "strings"
  15. "sync"
  16. "time"
  17. "git.aiterp.net/gisle/irc/ircutil"
  18. "git.aiterp.net/gisle/irc/isupport"
  19. )
  20. var supportedCaps = []string{
  21. "server-time",
  22. "cap-notify",
  23. "multi-prefix",
  24. "userhost-in-names",
  25. }
  26. // ErrNoConnection is returned if
  27. var ErrNoConnection = errors.New("irc: no connection")
  28. // A Client is an IRC client. You need to use New to construct it
  29. type Client struct {
  30. id string
  31. config Config
  32. mutex sync.RWMutex
  33. conn net.Conn
  34. ctx context.Context
  35. cancel context.CancelFunc
  36. events chan *Event
  37. sends chan string
  38. lastSend time.Time
  39. capEnabled map[string]bool
  40. capData map[string]string
  41. capsRequested []string
  42. nick string
  43. user string
  44. host string
  45. quit bool
  46. isupport isupport.ISupport
  47. values map[string]interface{}
  48. }
  49. // New creates a new client. The context can be context.Background if you want manually to
  50. // tear down clients upon quitting.
  51. func New(ctx context.Context, config Config) *Client {
  52. client := &Client{
  53. id: generateClientID(),
  54. values: make(map[string]interface{}),
  55. events: make(chan *Event, 64),
  56. sends: make(chan string, 64),
  57. capEnabled: make(map[string]bool),
  58. capData: make(map[string]string),
  59. config: config.WithDefaults(),
  60. }
  61. client.ctx, client.cancel = context.WithCancel(ctx)
  62. go client.handleEventLoop()
  63. go client.handleSendLoop()
  64. return client
  65. }
  66. // Context gets the client's context. It's cancelled if the parent context used
  67. // in New is, or Destroy is called.
  68. func (client *Client) Context() context.Context {
  69. return client.ctx
  70. }
  71. // Nick gets the nick of the client
  72. func (client *Client) Nick() string {
  73. client.mutex.RLock()
  74. defer client.mutex.RUnlock()
  75. return client.nick
  76. }
  77. // User gets the user/ident of the client
  78. func (client *Client) User() string {
  79. client.mutex.RLock()
  80. defer client.mutex.RUnlock()
  81. return client.user
  82. }
  83. // Host gets the hostname of the client
  84. func (client *Client) Host() string {
  85. client.mutex.RLock()
  86. defer client.mutex.RUnlock()
  87. return client.host
  88. }
  89. // ISupport gets the client's ISupport. This is mutable, and changes to it
  90. // *will* affect the client.
  91. func (client *Client) ISupport() *isupport.ISupport {
  92. return &client.isupport
  93. }
  94. // Connect connects to the server by addr.
  95. func (client *Client) Connect(addr string, ssl bool) (err error) {
  96. var conn net.Conn
  97. if client.Connected() {
  98. client.Disconnect()
  99. }
  100. client.isupport.Reset()
  101. client.mutex.Lock()
  102. client.quit = false
  103. client.mutex.Unlock()
  104. client.EmitSync(context.Background(), NewEvent("client", "connecting"))
  105. if ssl {
  106. conn, err = tls.Dial("tcp", addr, &tls.Config{
  107. InsecureSkipVerify: client.config.SkipSSLVerification,
  108. })
  109. if err != nil {
  110. return err
  111. }
  112. } else {
  113. conn, err = net.Dial("tcp", addr)
  114. if err != nil {
  115. return err
  116. }
  117. }
  118. client.Emit(NewEvent("client", "connect"))
  119. go func() {
  120. reader := bufio.NewReader(conn)
  121. replacer := strings.NewReplacer("\r", "", "\n", "")
  122. for {
  123. line, err := reader.ReadString('\n')
  124. if err != nil {
  125. break
  126. }
  127. line = replacer.Replace(line)
  128. event, err := ParsePacket(line)
  129. if err != nil {
  130. continue
  131. }
  132. client.Emit(event)
  133. }
  134. client.mutex.Lock()
  135. client.conn = nil
  136. client.mutex.Unlock()
  137. client.Emit(NewEvent("client", "disconnect"))
  138. }()
  139. client.mutex.Lock()
  140. client.conn = conn
  141. client.mutex.Unlock()
  142. return nil
  143. }
  144. // Disconnect disconnects from the server. It will either return the
  145. // close error, or ErrNoConnection if there is no connection
  146. func (client *Client) Disconnect() error {
  147. client.mutex.Lock()
  148. defer client.mutex.Unlock()
  149. if client.conn == nil {
  150. return ErrNoConnection
  151. }
  152. client.quit = true
  153. err := client.conn.Close()
  154. return err
  155. }
  156. // Connected returns true if the client has a connection
  157. func (client *Client) Connected() bool {
  158. client.mutex.RLock()
  159. defer client.mutex.RUnlock()
  160. return client.conn != nil
  161. }
  162. // Send sends a line to the server. A line-feed will be automatically added if one
  163. // is not provided.
  164. func (client *Client) Send(line string) error {
  165. client.mutex.RLock()
  166. conn := client.conn
  167. client.mutex.RUnlock()
  168. if conn == nil {
  169. return ErrNoConnection
  170. }
  171. if !strings.HasSuffix(line, "\n") {
  172. line += "\r\n"
  173. }
  174. _, err := conn.Write([]byte(line))
  175. if err != nil {
  176. client.EmitSafe(NewErrorEvent("network", err.Error()))
  177. client.Disconnect()
  178. }
  179. return err
  180. }
  181. // Sendf is Send with a fmt.Sprintf
  182. func (client *Client) Sendf(format string, a ...interface{}) error {
  183. return client.Send(fmt.Sprintf(format, a...))
  184. }
  185. // SendQueued appends a message to a queue that will only send 2 messages
  186. // per second to avoid flooding. If the queue is ull, a goroutine will be
  187. // spawned to queue it, so this function will always return immediately.
  188. // Order may not be guaranteed, however, but if you're sending 64 messages
  189. // at once that may not be your greatest concern.
  190. //
  191. // Failed sends will be discarded quietly to avoid a backup from being
  192. // thrown on a new connection.
  193. func (client *Client) SendQueued(line string) {
  194. select {
  195. case client.sends <- line:
  196. default:
  197. go func() { client.sends <- line }()
  198. }
  199. }
  200. // SendQueuedf is SendQueued with a fmt.Sprintf
  201. func (client *Client) SendQueuedf(format string, a ...interface{}) {
  202. client.SendQueued(fmt.Sprintf(format, a...))
  203. }
  204. // Emit sends an event through the client's event, and it will return immediately
  205. // unless the internal channel is filled up. The returned context can be used to
  206. // wait for the event, or the client's destruction.
  207. func (client *Client) Emit(event Event) context.Context {
  208. event.ctx, event.cancel = context.WithCancel(client.ctx)
  209. client.events <- &event
  210. return event.ctx
  211. }
  212. // EmitSafe is just like emit, but it will spin off a goroutine if the channel is full.
  213. // This lets it be called from other handlers without risking a deadlock. See Emit for
  214. // what the returned context is for.
  215. func (client *Client) EmitSafe(event Event) context.Context {
  216. event.ctx, event.cancel = context.WithCancel(client.ctx)
  217. select {
  218. case client.events <- &event:
  219. default:
  220. go func() { client.events <- &event }()
  221. }
  222. return event.ctx
  223. }
  224. // EmitSync emits an event and waits for either its context to complete or the one
  225. // passed to it (e.g. a request's context). It's a shorthand for Emit with its
  226. // return value used in a `select` along with a passed context.
  227. func (client *Client) EmitSync(ctx context.Context, event Event) (err error) {
  228. eventCtx := client.Emit(event)
  229. select {
  230. case <-eventCtx.Done():
  231. {
  232. if err := eventCtx.Err(); err != context.Canceled {
  233. return err
  234. }
  235. return nil
  236. }
  237. case <-ctx.Done():
  238. {
  239. return ctx.Err()
  240. }
  241. }
  242. }
  243. // Value gets a client value.
  244. func (client *Client) Value(key string) (v interface{}, ok bool) {
  245. client.mutex.RLock()
  246. v, ok = client.values[key]
  247. client.mutex.RUnlock()
  248. return
  249. }
  250. // SetValue sets a client value.
  251. func (client *Client) SetValue(key string, value interface{}) {
  252. client.mutex.Lock()
  253. client.values[key] = value
  254. client.mutex.Unlock()
  255. }
  256. // Destroy destroys the client, which will lead to a disconnect. Cancelling the
  257. // parent context will do the same.
  258. func (client *Client) Destroy() {
  259. client.Disconnect()
  260. client.cancel()
  261. close(client.sends)
  262. close(client.events)
  263. }
  264. // Destroyed returns true if the client has been destroyed, either by
  265. // Destroy or the parent context.
  266. func (client *Client) Destroyed() bool {
  267. select {
  268. case <-client.ctx.Done():
  269. return true
  270. default:
  271. return false
  272. }
  273. }
  274. // PrivmsgOverhead returns the overhead on a privmsg to the target. If `action` is true,
  275. // it will also count the extra overhead of a CTCP ACTION.
  276. func (client *Client) PrivmsgOverhead(targetName string, action bool) int {
  277. client.mutex.RLock()
  278. defer client.mutex.RUnlock()
  279. // Return a really safe estimate if user or host is missing.
  280. if client.user == "" || client.host == "" {
  281. return 200
  282. }
  283. return ircutil.MessageOverhead(client.nick, client.user, client.host, targetName, action)
  284. }
  285. // Join joins one or more channels without a key.
  286. func (client *Client) Join(channels ...string) error {
  287. return client.Sendf("JOIN %s", strings.Join(channels, ","))
  288. }
  289. func (client *Client) handleEventLoop() {
  290. ticker := time.NewTicker(time.Second * 30)
  291. for {
  292. select {
  293. case event, ok := <-client.events:
  294. {
  295. if !ok {
  296. goto end
  297. }
  298. client.handleEvent(event)
  299. emit(event, client)
  300. event.cancel()
  301. }
  302. case <-ticker.C:
  303. {
  304. event := NewEvent("client", "tick")
  305. event.ctx, event.cancel = context.WithCancel(client.ctx)
  306. client.handleEvent(&event)
  307. emit(&event, client)
  308. event.cancel()
  309. }
  310. case <-client.ctx.Done():
  311. {
  312. goto end
  313. }
  314. }
  315. }
  316. end:
  317. ticker.Stop()
  318. client.Disconnect()
  319. event := NewEvent("client", "destroy")
  320. event.ctx, event.cancel = context.WithCancel(client.ctx)
  321. client.handleEvent(&event)
  322. emit(&event, client)
  323. event.cancel()
  324. }
  325. func (client *Client) handleSendLoop() {
  326. lastRefresh := time.Time{}
  327. queue := 2
  328. for line := range client.sends {
  329. now := time.Now()
  330. deltaTime := now.Sub(lastRefresh)
  331. if deltaTime < time.Second {
  332. queue--
  333. if queue <= 0 {
  334. time.Sleep(time.Second - deltaTime)
  335. lastRefresh = now
  336. queue = 0
  337. }
  338. } else {
  339. lastRefresh = now
  340. }
  341. client.Send(line)
  342. }
  343. }
  344. // handleEvent is always first and gets to break a few rules.
  345. func (client *Client) handleEvent(event *Event) {
  346. // IRCv3 `server-time`
  347. if timeTag, ok := event.Tags["time"]; ok {
  348. serverTime, err := time.Parse(time.RFC3339Nano, timeTag)
  349. if err == nil && serverTime.Year() > 2000 {
  350. event.Time = serverTime
  351. }
  352. }
  353. switch event.name {
  354. // Ping Pong
  355. case "hook.tick":
  356. {
  357. client.mutex.RLock()
  358. lastSend := time.Since(client.lastSend)
  359. client.mutex.RUnlock()
  360. if lastSend > time.Second*120 {
  361. client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
  362. }
  363. }
  364. case "packet.ping":
  365. {
  366. message := "PONG"
  367. for _, arg := range event.Args {
  368. message += " " + arg
  369. }
  370. if event.Text != "" {
  371. message += " :" + event.Text
  372. }
  373. client.Send(message + "")
  374. }
  375. // Client Registration
  376. case "client.connect":
  377. {
  378. client.Send("CAP LS 302")
  379. if client.config.Password != "" {
  380. client.Sendf("PASS :%s", client.config.Password)
  381. }
  382. nick := client.config.Nick
  383. client.mutex.RLock()
  384. if client.nick != "" {
  385. nick = client.nick
  386. }
  387. client.mutex.RUnlock()
  388. client.Sendf("NICK %s", nick)
  389. client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
  390. }
  391. case "packet.001":
  392. {
  393. client.mutex.Lock()
  394. client.nick = event.Args[0]
  395. client.mutex.Unlock()
  396. client.Sendf("WHO %s", event.Args[0])
  397. }
  398. case "packet.443":
  399. {
  400. client.mutex.RLock()
  401. hasRegistered := client.nick != ""
  402. client.mutex.RUnlock()
  403. if !hasRegistered {
  404. nick := event.Args[1]
  405. // "AltN" -> "AltN+1", ...
  406. prev := client.config.Nick
  407. sent := false
  408. for _, alt := range client.config.Alternatives {
  409. if nick == prev {
  410. client.Sendf("NICK %s", alt)
  411. sent = true
  412. break
  413. }
  414. prev = alt
  415. }
  416. if !sent {
  417. // "LastAlt" -> "Nick23962"
  418. client.Sendf("NICK %s%05d", client.config.Nick, mathRand.Int31n(99999))
  419. }
  420. }
  421. }
  422. // Handle ISupport
  423. case "packet.005":
  424. {
  425. for _, token := range event.Args[1:] {
  426. kvpair := strings.Split(token, "=")
  427. if len(kvpair) == 2 {
  428. client.isupport.Set(kvpair[0], kvpair[1])
  429. } else {
  430. client.isupport.Set(kvpair[0], "")
  431. }
  432. }
  433. }
  434. // Capability negotiation
  435. case "packet.cap":
  436. {
  437. capCommand := event.Args[1]
  438. capTokens := strings.Split(event.Text, " ")
  439. switch capCommand {
  440. case "LS":
  441. {
  442. for _, token := range capTokens {
  443. split := strings.SplitN(token, "=", 2)
  444. key := split[0]
  445. if len(key) == 0 {
  446. continue
  447. }
  448. if len(split) == 2 {
  449. client.capData[key] = split[1]
  450. }
  451. for i := range supportedCaps {
  452. if supportedCaps[i] == token {
  453. client.mutex.Lock()
  454. client.capsRequested = append(client.capsRequested, token)
  455. client.mutex.Unlock()
  456. break
  457. }
  458. }
  459. }
  460. if len(event.Args) < 3 || event.Args[2] != "*" {
  461. client.mutex.RLock()
  462. requestedCount := len(client.capsRequested)
  463. client.mutex.RUnlock()
  464. if requestedCount > 0 {
  465. client.mutex.RLock()
  466. requestedCaps := strings.Join(client.capsRequested, " ")
  467. client.mutex.RUnlock()
  468. client.Send("CAP REQ :" + requestedCaps)
  469. } else {
  470. client.Send("CAP END")
  471. }
  472. }
  473. }
  474. case "ACK":
  475. {
  476. for _, token := range capTokens {
  477. client.mutex.Lock()
  478. if client.capEnabled[token] {
  479. client.capEnabled[token] = true
  480. }
  481. client.mutex.Unlock()
  482. }
  483. client.Send("CAP END")
  484. }
  485. case "NAK":
  486. {
  487. // Remove offenders
  488. for _, token := range capTokens {
  489. client.mutex.Lock()
  490. for i := range client.capsRequested {
  491. if token == client.capsRequested[i] {
  492. client.capsRequested = append(client.capsRequested[:i], client.capsRequested[i+1:]...)
  493. break
  494. }
  495. }
  496. client.mutex.Unlock()
  497. }
  498. client.mutex.RLock()
  499. requestedCaps := strings.Join(client.capsRequested, " ")
  500. client.mutex.RUnlock()
  501. client.Send("CAP REQ :" + requestedCaps)
  502. }
  503. case "NEW":
  504. {
  505. requests := make([]string, 0, len(capTokens))
  506. for _, token := range capTokens {
  507. for i := range supportedCaps {
  508. if supportedCaps[i] == token {
  509. requests = append(requests, token)
  510. }
  511. }
  512. }
  513. if len(requests) > 0 {
  514. client.Send("CAP REQ :" + strings.Join(requests, " "))
  515. }
  516. }
  517. case "DEL":
  518. {
  519. for _, token := range capTokens {
  520. client.mutex.Lock()
  521. if client.capEnabled[token] {
  522. client.capEnabled[token] = false
  523. }
  524. client.mutex.Unlock()
  525. }
  526. }
  527. }
  528. }
  529. // User/host detection
  530. case "packet.352": // WHO reply
  531. {
  532. // Example args: test * ~irce 127.0.0.1 localhost.localnetwork Gissleh H :0 ...
  533. nick := event.Args[5]
  534. user := event.Args[2]
  535. host := event.Args[3]
  536. if nick == client.nick {
  537. client.mutex.Lock()
  538. client.user = user
  539. client.host = host
  540. client.mutex.Unlock()
  541. }
  542. }
  543. case "packet.chghost":
  544. {
  545. if event.Nick == client.nick {
  546. client.mutex.Lock()
  547. client.user = event.Args[1]
  548. client.host = event.Args[2]
  549. client.mutex.Unlock()
  550. }
  551. }
  552. }
  553. }
  554. func generateClientID() string {
  555. bytes := make([]byte, 12)
  556. _, err := rand.Read(bytes)
  557. // Ugly fallback if crypto rand doesn't work.
  558. if err != nil {
  559. rng := mathRand.NewSource(time.Now().UnixNano())
  560. result := strconv.FormatInt(rng.Int63(), 16)
  561. for len(result) < 24 {
  562. result += strconv.FormatInt(rng.Int63(), 16)
  563. }
  564. return result[:24]
  565. }
  566. binary.BigEndian.PutUint32(bytes, uint32(time.Now().Unix()))
  567. return hex.EncodeToString(bytes)
  568. }