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.

1309 lines
30 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
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
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
6 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
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
6 years ago
6 years ago
6 years ago
6 years ago
6 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. "sort"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "time"
  18. "github.com/gissleh/irc/ircutil"
  19. "github.com/gissleh/irc/isupport"
  20. "github.com/gissleh/irc/list"
  21. )
  22. var supportedCaps = []string{
  23. "server-time",
  24. "cap-notify",
  25. "multi-prefix",
  26. "userhost-in-names",
  27. "account-notify",
  28. "away-notify",
  29. "extended-join",
  30. "chghost",
  31. "account-tag",
  32. "echo-message",
  33. }
  34. // ErrNoConnection is returned if you try to do something requiring a connection,
  35. // but there is none.
  36. var ErrNoConnection = errors.New("irc: no connection")
  37. // ErrTargetAlreadyAdded is returned by Client.AddTarget if that target has already been
  38. // added to the client.
  39. var ErrTargetAlreadyAdded = errors.New("irc: target already added")
  40. // ErrTargetConflict is returned by Client.AddTarget if there already exists a target
  41. // matching the name and kind.
  42. var ErrTargetConflict = errors.New("irc: target name and kind match existing target")
  43. // ErrTargetNotFound is returned by Client.RemoveTarget if the target is not part of
  44. // the client's target list
  45. var ErrTargetNotFound = errors.New("irc: target not found")
  46. // ErrTargetIsStatus is returned by Client.RemoveTarget if the target is the client's
  47. // status target
  48. var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
  49. // A Client is an IRC client. You need to use New to construct it
  50. type Client struct {
  51. id string
  52. config Config
  53. mutex sync.RWMutex
  54. conn net.Conn
  55. ctx context.Context
  56. cancel context.CancelFunc
  57. events chan *Event
  58. sends chan string
  59. lastSend time.Time
  60. capEnabled map[string]bool
  61. capData map[string]string
  62. capsRequested []string
  63. nick string
  64. user string
  65. host string
  66. quit bool
  67. ready bool
  68. isupport isupport.ISupport
  69. values map[string]interface{}
  70. status *Status
  71. targets []Target
  72. targetIds map[Target]string
  73. handlers []Handler
  74. }
  75. // New creates a new client. The context can be context.Background if you want manually to
  76. // tear down clients upon quitting.
  77. func New(ctx context.Context, config Config) *Client {
  78. client := &Client{
  79. id: generateClientID(),
  80. values: make(map[string]interface{}),
  81. events: make(chan *Event, 64),
  82. sends: make(chan string, 64),
  83. capEnabled: make(map[string]bool),
  84. capData: make(map[string]string),
  85. config: config.WithDefaults(),
  86. targetIds: make(map[Target]string, 16),
  87. status: &Status{},
  88. }
  89. _, _ = client.AddTarget(client.status)
  90. client.ctx, client.cancel = context.WithCancel(ctx)
  91. go client.handleEventLoop()
  92. go client.handleSendLoop()
  93. return client
  94. }
  95. // Context gets the client's context. It's cancelled if the parent context used
  96. // in New is, or Destroy is called.
  97. func (client *Client) Context() context.Context {
  98. return client.ctx
  99. }
  100. // ID gets the unique identifier for the client, which could be used in data structures
  101. func (client *Client) ID() string {
  102. client.mutex.RLock()
  103. defer client.mutex.RUnlock()
  104. return client.id
  105. }
  106. // Nick gets the nick of the client
  107. func (client *Client) Nick() string {
  108. client.mutex.RLock()
  109. defer client.mutex.RUnlock()
  110. return client.nick
  111. }
  112. // User gets the user/ident of the client
  113. func (client *Client) User() string {
  114. client.mutex.RLock()
  115. defer client.mutex.RUnlock()
  116. return client.user
  117. }
  118. // Host gets the hostname of the client
  119. func (client *Client) Host() string {
  120. client.mutex.RLock()
  121. defer client.mutex.RUnlock()
  122. return client.host
  123. }
  124. // ISupport gets the client's ISupport. This is mutable, and changes to it
  125. // *will* affect the client.
  126. func (client *Client) ISupport() *isupport.ISupport {
  127. return &client.isupport
  128. }
  129. // CapEnabled returns whether an IRCv3 capability is enabled.
  130. func (client *Client) CapEnabled(cap string) bool {
  131. client.mutex.RLock()
  132. defer client.mutex.RUnlock()
  133. return client.capEnabled[cap]
  134. }
  135. // Ready returns true if the client is marked as ready, which means that it has received the MOTD.
  136. func (client *Client) Ready() bool {
  137. client.mutex.RLock()
  138. defer client.mutex.RUnlock()
  139. return client.ready
  140. }
  141. func (client *Client) State() ClientState {
  142. client.mutex.RLock()
  143. state := ClientState{
  144. Nick: client.nick,
  145. User: client.user,
  146. Host: client.host,
  147. Connected: client.conn != nil,
  148. Ready: client.ready,
  149. ISupport: client.isupport.State(),
  150. Caps: make([]string, 0, len(client.capEnabled)),
  151. Targets: make([]TargetState, 0, len(client.targets)),
  152. }
  153. for key, enabled := range client.capEnabled {
  154. if enabled {
  155. state.Caps = append(state.Caps, key)
  156. }
  157. }
  158. sort.Strings(state.Caps)
  159. for _, target := range client.targets {
  160. tstate := target.State()
  161. tstate.ID = client.targetIds[target]
  162. state.Targets = append(state.Targets, tstate)
  163. }
  164. client.mutex.RUnlock()
  165. return state
  166. }
  167. // Connect connects to the server by addr.
  168. func (client *Client) Connect(addr string, ssl bool) (err error) {
  169. var conn net.Conn
  170. if client.Connected() {
  171. _ = client.Disconnect()
  172. }
  173. client.isupport.Reset()
  174. client.mutex.Lock()
  175. client.quit = false
  176. client.mutex.Unlock()
  177. _ = client.EmitSync(context.Background(), NewEvent("client", "connecting"))
  178. if ssl {
  179. conn, err = tls.Dial("tcp", addr, &tls.Config{
  180. InsecureSkipVerify: client.config.SkipSSLVerification,
  181. })
  182. if err != nil {
  183. return err
  184. }
  185. } else {
  186. conn, err = net.Dial("tcp", addr)
  187. if err != nil {
  188. return err
  189. }
  190. }
  191. client.Emit(NewEvent("client", "connect"))
  192. go func() {
  193. reader := bufio.NewReader(conn)
  194. replacer := strings.NewReplacer("\r", "", "\n", "")
  195. for {
  196. line, err := reader.ReadString('\n')
  197. if err != nil {
  198. break
  199. }
  200. line = replacer.Replace(line)
  201. event, err := ParsePacket(line)
  202. if err != nil {
  203. continue
  204. }
  205. client.Emit(event)
  206. }
  207. client.mutex.Lock()
  208. client.conn = nil
  209. client.ready = false
  210. client.mutex.Unlock()
  211. client.Emit(NewEvent("client", "disconnect"))
  212. }()
  213. client.mutex.Lock()
  214. client.conn = conn
  215. client.mutex.Unlock()
  216. return nil
  217. }
  218. // Disconnect disconnects from the server. It will either return the
  219. // close error, or ErrNoConnection if there is no connection
  220. func (client *Client) Disconnect() error {
  221. client.mutex.Lock()
  222. defer client.mutex.Unlock()
  223. if client.conn == nil {
  224. return ErrNoConnection
  225. }
  226. client.quit = true
  227. return client.conn.Close()
  228. }
  229. // Connected returns true if the client has a connection
  230. func (client *Client) Connected() bool {
  231. client.mutex.RLock()
  232. defer client.mutex.RUnlock()
  233. return client.conn != nil
  234. }
  235. // Send sends a line to the server. A line-feed will be automatically added if one
  236. // is not provided.
  237. func (client *Client) Send(line string) error {
  238. client.mutex.RLock()
  239. conn := client.conn
  240. client.mutex.RUnlock()
  241. if conn == nil {
  242. return ErrNoConnection
  243. }
  244. if !strings.HasSuffix(line, "\n") {
  245. line += "\r\n"
  246. }
  247. _, err := conn.Write([]byte(line))
  248. if err != nil {
  249. client.EmitNonBlocking(NewErrorEvent("network", err.Error()))
  250. _ = client.Disconnect()
  251. }
  252. return err
  253. }
  254. // Sendf is Send with a fmt.Sprintf
  255. func (client *Client) Sendf(format string, a ...interface{}) error {
  256. return client.Send(fmt.Sprintf(format, a...))
  257. }
  258. // SendQueued appends a message to a queue that will only send 2 messages
  259. // per second to avoid flooding. If the queue is ull, a goroutine will be
  260. // spawned to queue it, so this function will always return immediately.
  261. // Order may not be guaranteed, however, but if you're sending 64 messages
  262. // at once that may not be your greatest concern.
  263. //
  264. // Failed sends will be discarded quietly to avoid a backup from being
  265. // thrown on a new connection.
  266. func (client *Client) SendQueued(line string) {
  267. select {
  268. case client.sends <- line:
  269. default:
  270. go func() { client.sends <- line }()
  271. }
  272. }
  273. // SendQueuedf is SendQueued with a fmt.Sprintf
  274. func (client *Client) SendQueuedf(format string, a ...interface{}) {
  275. client.SendQueued(fmt.Sprintf(format, a...))
  276. }
  277. // SendCTCP sends a queued message with the following CTCP verb and text. If reply is true,
  278. // it will use a NOTICE instead of PRIVMSG.
  279. func (client *Client) SendCTCP(verb, targetName string, reply bool, text string) {
  280. ircVerb := "PRIVMSG"
  281. if reply {
  282. ircVerb = "NOTICE"
  283. }
  284. client.SendQueuedf("%s %s :\x01%s %s\x01", ircVerb, targetName, verb, text)
  285. }
  286. // SendCTCPf is SendCTCP with a fmt.Sprintf
  287. func (client *Client) SendCTCPf(verb, targetName string, reply bool, format string, a ...interface{}) {
  288. client.SendCTCP(verb, targetName, reply, fmt.Sprintf(format, a...))
  289. }
  290. // Say sends a PRIVMSG with the target name and text, cutting the message if it gets too long.
  291. func (client *Client) Say(targetName string, text string) {
  292. overhead := client.PrivmsgOverhead(targetName, false)
  293. cuts := ircutil.CutMessage(text, overhead)
  294. for _, cut := range cuts {
  295. client.SendQueuedf("PRIVMSG %s :%s", targetName, cut)
  296. }
  297. }
  298. // Sayf is Say with a fmt.Sprintf.
  299. func (client *Client) Sayf(targetName string, format string, a ...interface{}) {
  300. client.Say(targetName, fmt.Sprintf(format, a...))
  301. }
  302. // Describe sends a CTCP ACTION with the target name and text, cutting the message if it gets too long.
  303. func (client *Client) Describe(targetName string, text string) {
  304. overhead := client.PrivmsgOverhead(targetName, true)
  305. cuts := ircutil.CutMessage(text, overhead)
  306. for _, cut := range cuts {
  307. client.SendQueuedf("PRIVMSG %s :\x01ACTION %s\x01", targetName, cut)
  308. }
  309. }
  310. // Describef is Describe with a fmt.Sprintf.
  311. func (client *Client) Describef(targetName string, format string, a ...interface{}) {
  312. client.Describe(targetName, fmt.Sprintf(format, a...))
  313. }
  314. // Emit sends an event through the client's event, and it will return immediately
  315. // unless the internal channel is filled up. The returned context can be used to
  316. // wait for the event, or the client's destruction.
  317. func (client *Client) Emit(event Event) context.Context {
  318. event.ctx, event.cancel = context.WithCancel(client.ctx)
  319. client.events <- &event
  320. return event.ctx
  321. }
  322. // EmitNonBlocking is just like emitInGlobalHandlers, but it will spin off a goroutine if the channel is full.
  323. // This lets it be called from other handlers without ever blocking. See Emit for what the
  324. // returned context is for.
  325. func (client *Client) EmitNonBlocking(event Event) context.Context {
  326. event.ctx, event.cancel = context.WithCancel(client.ctx)
  327. select {
  328. case client.events <- &event:
  329. default:
  330. go func() { client.events <- &event }()
  331. }
  332. return event.ctx
  333. }
  334. // EmitSync emits an event and waits for either its context to complete or the one
  335. // passed to it (e.g. a request's context). It's a shorthand for Emit with its
  336. // return value used in a `select` along with a passed context.
  337. func (client *Client) EmitSync(ctx context.Context, event Event) (err error) {
  338. eventCtx := client.Emit(event)
  339. select {
  340. case <-eventCtx.Done():
  341. {
  342. if err := eventCtx.Err(); err != context.Canceled {
  343. return err
  344. }
  345. return nil
  346. }
  347. case <-ctx.Done():
  348. {
  349. return ctx.Err()
  350. }
  351. }
  352. }
  353. // EmitInput emits an input event parsed from the line.
  354. func (client *Client) EmitInput(line string, target Target) context.Context {
  355. event := ParseInput(line)
  356. if target != nil {
  357. client.mutex.RLock()
  358. event.targets = append(event.targets, target)
  359. event.targetIds[target] = client.targetIds[target]
  360. client.mutex.RUnlock()
  361. } else {
  362. client.mutex.RLock()
  363. event.targets = append(event.targets, client.status)
  364. event.targetIds[client.status] = client.targetIds[client.status]
  365. client.mutex.RUnlock()
  366. }
  367. return client.Emit(event)
  368. }
  369. // Value gets a client value.
  370. func (client *Client) Value(key string) interface{} {
  371. client.mutex.RLock()
  372. defer client.mutex.RUnlock()
  373. return client.values[key]
  374. }
  375. // SetValue sets a client value.
  376. func (client *Client) SetValue(key string, value interface{}) {
  377. client.mutex.Lock()
  378. client.values[key] = value
  379. client.mutex.Unlock()
  380. }
  381. // Destroy destroys the client, which will lead to a disconnect. Cancelling the
  382. // parent context will do the same.
  383. func (client *Client) Destroy() {
  384. _ = client.Disconnect()
  385. client.cancel()
  386. close(client.sends)
  387. close(client.events)
  388. }
  389. // Destroyed returns true if the client has been destroyed, either by
  390. // Destroy or the parent context.
  391. func (client *Client) Destroyed() bool {
  392. select {
  393. case <-client.ctx.Done():
  394. return true
  395. default:
  396. return false
  397. }
  398. }
  399. // PrivmsgOverhead returns the overhead on a privmsg to the target. If `action` is true,
  400. // it will also count the extra overhead of a CTCP ACTION.
  401. func (client *Client) PrivmsgOverhead(targetName string, action bool) int {
  402. client.mutex.RLock()
  403. defer client.mutex.RUnlock()
  404. // Return a really safe estimate if user or host is missing.
  405. if client.user == "" || client.host == "" {
  406. return 200
  407. }
  408. return ircutil.MessageOverhead(client.nick, client.user, client.host, targetName, action)
  409. }
  410. // Join joins one or more channels without a key.
  411. func (client *Client) Join(channels ...string) {
  412. client.SendQueuedf("JOIN %s", strings.Join(channels, ","))
  413. }
  414. // Part parts one or more channels.
  415. func (client *Client) Part(channels ...string) {
  416. client.SendQueuedf("PART %s", strings.Join(channels, ","))
  417. }
  418. // Target gets a target by kind and name
  419. func (client *Client) Target(kind string, name string) Target {
  420. client.mutex.RLock()
  421. defer client.mutex.RUnlock()
  422. for _, target := range client.targets {
  423. if target.Kind() == kind && target.Name() == name {
  424. return target
  425. }
  426. }
  427. return nil
  428. }
  429. // Targets gets all targets of the given kinds.
  430. func (client *Client) Targets(kinds ...string) []Target {
  431. if len(kinds) == 0 {
  432. client.mutex.Lock()
  433. targets := make([]Target, len(client.targets))
  434. copy(targets, client.targets)
  435. client.mutex.Unlock()
  436. return targets
  437. }
  438. client.mutex.Lock()
  439. targets := make([]Target, 0, len(client.targets))
  440. for _, target := range client.targets {
  441. for _, kind := range kinds {
  442. if target.Kind() == kind {
  443. targets = append(targets, target)
  444. break
  445. }
  446. }
  447. }
  448. client.mutex.Unlock()
  449. return targets
  450. }
  451. // Status gets the client's status target.
  452. func (client *Client) Status() *Status {
  453. return client.status
  454. }
  455. // Channel is a shorthand for getting a channel target and type asserting it.
  456. func (client *Client) Channel(name string) *Channel {
  457. target := client.Target("channel", name)
  458. if target == nil {
  459. return nil
  460. }
  461. return target.(*Channel)
  462. }
  463. // Channels gets all channel targets the client has.
  464. func (client *Client) Channels() []*Channel {
  465. targets := client.Targets("channel")
  466. channels := make([]*Channel, len(targets))
  467. for i := range targets {
  468. channels[i] = targets[i].(*Channel)
  469. }
  470. return channels
  471. }
  472. // Query is a shorthand for getting a query target and type asserting it.
  473. func (client *Client) Query(name string) *Query {
  474. target := client.Target("query", name)
  475. if target == nil {
  476. return nil
  477. }
  478. return target.(*Query)
  479. }
  480. // AddTarget adds a target to the client, generating a unique ID for it.
  481. func (client *Client) AddTarget(target Target) (id string, err error) {
  482. client.mutex.Lock()
  483. defer client.mutex.Unlock()
  484. for i := range client.targets {
  485. if target == client.targets[i] {
  486. err = ErrTargetAlreadyAdded
  487. return
  488. } else if target.Kind() == client.targets[i].Kind() && target.Name() == client.targets[i].Name() {
  489. err = ErrTargetConflict
  490. return
  491. }
  492. }
  493. id = generateClientID()
  494. client.targets = append(client.targets, target)
  495. client.targetIds[target] = id
  496. return
  497. }
  498. // RemoveTarget removes a target to the client
  499. func (client *Client) RemoveTarget(target Target) (id string, err error) {
  500. if target == client.status {
  501. return "", ErrTargetIsStatus
  502. }
  503. client.mutex.Lock()
  504. defer client.mutex.Unlock()
  505. for i := range client.targets {
  506. if target == client.targets[i] {
  507. id = client.targetIds[target]
  508. client.targets[i] = client.targets[len(client.targets)-1]
  509. client.targets = client.targets[:len(client.targets)-1]
  510. delete(client.targetIds, target)
  511. // Ensure the channel has been parted
  512. if channel, ok := target.(*Channel); ok && !channel.parted {
  513. client.SendQueuedf("PART %s", channel.Name())
  514. }
  515. return
  516. }
  517. }
  518. err = ErrTargetNotFound
  519. return
  520. }
  521. // FindUser checks each channel to find user info about a user.
  522. func (client *Client) FindUser(nick string) (u list.User, ok bool) {
  523. client.mutex.RLock()
  524. defer client.mutex.RUnlock()
  525. for _, target := range client.targets {
  526. channel, ok := target.(*Channel)
  527. if !ok {
  528. continue
  529. }
  530. user, ok := channel.UserList().User(nick)
  531. if !ok {
  532. continue
  533. }
  534. return user, true
  535. }
  536. return list.User{}, false
  537. }
  538. // AddHandler adds a handler. This is thread safe, unlike adding global handlers.
  539. func (client *Client) AddHandler(handler Handler) {
  540. client.mutex.Lock()
  541. client.handlers = append(client.handlers[:0], client.handlers...)
  542. client.handlers = append(client.handlers, handler)
  543. client.mutex.Unlock()
  544. }
  545. func (client *Client) handleEventLoop() {
  546. ticker := time.NewTicker(time.Second * 30)
  547. for {
  548. select {
  549. case event, ok := <-client.events:
  550. {
  551. if !ok {
  552. goto end
  553. }
  554. client.handleEvent(event)
  555. // Turn an unhandled input into a raw command.
  556. if event.kind == "input" && !event.preventedDefault {
  557. client.SendQueued(strings.ToUpper(event.verb) + " " + event.Text)
  558. }
  559. event.cancel()
  560. }
  561. case <-ticker.C:
  562. {
  563. event := NewEvent("client", "tick")
  564. event.ctx, event.cancel = context.WithCancel(client.ctx)
  565. client.handleEvent(&event)
  566. event.cancel()
  567. }
  568. case <-client.ctx.Done():
  569. {
  570. goto end
  571. }
  572. }
  573. }
  574. end:
  575. ticker.Stop()
  576. _ = client.Disconnect()
  577. event := NewEvent("client", "destroy")
  578. event.ctx, event.cancel = context.WithCancel(client.ctx)
  579. client.handleEvent(&event)
  580. event.cancel()
  581. }
  582. func (client *Client) handleSendLoop() {
  583. lastRefresh := time.Time{}
  584. queue := client.config.SendRate
  585. for line := range client.sends {
  586. now := time.Now()
  587. deltaTime := now.Sub(lastRefresh)
  588. if deltaTime < time.Second {
  589. queue--
  590. if queue <= 0 {
  591. time.Sleep(time.Second - deltaTime)
  592. lastRefresh = now
  593. queue = client.config.SendRate - 1
  594. }
  595. } else {
  596. lastRefresh = now
  597. queue = client.config.SendRate - 1
  598. }
  599. _ = client.Send(line)
  600. }
  601. }
  602. // handleEvent is always first and gets to break a few rules.
  603. func (client *Client) handleEvent(event *Event) {
  604. // IRCv3 `server-time`
  605. if timeTag, ok := event.Tags["time"]; ok {
  606. serverTime, err := time.Parse(time.RFC3339Nano, timeTag)
  607. if err == nil && serverTime.Year() > 2000 {
  608. event.Time = serverTime
  609. }
  610. }
  611. // For events that were created with targets, handle them now there now.
  612. for _, target := range event.targets {
  613. target.Handle(event, client)
  614. }
  615. switch event.name {
  616. // Ping Pong
  617. case "hook.tick":
  618. {
  619. client.mutex.RLock()
  620. lastSend := time.Since(client.lastSend)
  621. client.mutex.RUnlock()
  622. if lastSend > time.Second*120 {
  623. _ = client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
  624. }
  625. }
  626. case "packet.ping":
  627. {
  628. message := "PONG"
  629. for _, arg := range event.Args {
  630. message += " " + arg
  631. }
  632. if event.Text != "" {
  633. message += " :" + event.Text
  634. }
  635. _ = client.Send(message)
  636. }
  637. // Client Registration
  638. case "client.connect":
  639. {
  640. // Clear enabled caps and initiate negotiation.
  641. client.mutex.Lock()
  642. for key := range client.capEnabled {
  643. delete(client.capEnabled, key)
  644. }
  645. client.mutex.Unlock()
  646. _ = client.Send("CAP LS 302")
  647. // Send server password if configured.
  648. if client.config.Password != "" {
  649. _ = client.Sendf("PASS :%s", client.config.Password)
  650. }
  651. // Reuse nick or get from config
  652. nick := client.config.Nick
  653. client.mutex.RLock()
  654. if client.nick != "" {
  655. nick = client.nick
  656. }
  657. client.mutex.RUnlock()
  658. // Start registration.
  659. _ = client.Sendf("NICK %s", nick)
  660. _ = client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
  661. }
  662. // Welcome message
  663. case "packet.001":
  664. {
  665. client.mutex.Lock()
  666. client.nick = event.Args[0]
  667. client.mutex.Unlock()
  668. // Send a WHO right away to gather enough client information for precise message cutting.
  669. _ = client.Sendf("WHO %s", event.Args[0])
  670. }
  671. // Nick rotation
  672. case "packet.431", "packet.432", "packet.433", "packet.436":
  673. {
  674. client.mutex.RLock()
  675. hasRegistered := client.nick != ""
  676. client.mutex.RUnlock()
  677. if !hasRegistered {
  678. nick := event.Args[1]
  679. // "AltN" -> "AltN+1", ...
  680. prev := client.config.Nick
  681. sent := false
  682. for _, alt := range client.config.Alternatives {
  683. if nick == prev {
  684. _ = client.Sendf("NICK %s", alt)
  685. sent = true
  686. break
  687. }
  688. prev = alt
  689. }
  690. if !sent {
  691. // "LastAlt" -> "Nick23962"
  692. _ = client.Sendf("NICK %s%05d", client.config.Nick, mathRand.Int31n(99999))
  693. }
  694. }
  695. }
  696. case "packet.nick":
  697. {
  698. client.handleInTargets(event.Nick, event)
  699. if event.Nick == client.nick {
  700. client.SetValue("nick", event.Arg(0))
  701. }
  702. }
  703. // AddHandler ISupport
  704. case "packet.005":
  705. {
  706. for _, token := range event.Args[1:] {
  707. kvpair := strings.Split(token, "=")
  708. if len(kvpair) == 2 {
  709. client.isupport.Set(kvpair[0], kvpair[1])
  710. } else {
  711. client.isupport.Set(kvpair[0], "")
  712. }
  713. }
  714. }
  715. // Capability negotiation
  716. case "packet.cap":
  717. {
  718. capCommand := event.Args[1]
  719. capTokens := strings.Split(event.Text, " ")
  720. switch capCommand {
  721. case "LS":
  722. {
  723. for _, token := range capTokens {
  724. split := strings.SplitN(token, "=", 2)
  725. key := split[0]
  726. if len(key) == 0 {
  727. continue
  728. }
  729. if len(split) == 2 {
  730. client.capData[key] = split[1]
  731. }
  732. for i := range supportedCaps {
  733. if supportedCaps[i] == token {
  734. client.mutex.Lock()
  735. client.capsRequested = append(client.capsRequested, token)
  736. client.mutex.Unlock()
  737. break
  738. }
  739. }
  740. }
  741. if len(event.Args) < 3 || event.Args[2] != "*" {
  742. client.mutex.RLock()
  743. requestedCount := len(client.capsRequested)
  744. client.mutex.RUnlock()
  745. if requestedCount > 0 {
  746. client.mutex.RLock()
  747. requestedCaps := strings.Join(client.capsRequested, " ")
  748. client.mutex.RUnlock()
  749. _ = client.Send("CAP REQ :" + requestedCaps)
  750. } else {
  751. _ = client.Send("CAP END")
  752. }
  753. }
  754. }
  755. case "ACK":
  756. {
  757. for _, token := range capTokens {
  758. client.mutex.Lock()
  759. if !client.capEnabled[token] {
  760. client.capEnabled[token] = true
  761. }
  762. client.mutex.Unlock()
  763. }
  764. _ = client.Send("CAP END")
  765. }
  766. case "NAK":
  767. {
  768. // Remove offenders
  769. for _, token := range capTokens {
  770. client.mutex.Lock()
  771. for i := range client.capsRequested {
  772. if token == client.capsRequested[i] {
  773. client.capsRequested = append(client.capsRequested[:i], client.capsRequested[i+1:]...)
  774. break
  775. }
  776. }
  777. client.mutex.Unlock()
  778. }
  779. client.mutex.RLock()
  780. requestedCaps := strings.Join(client.capsRequested, " ")
  781. client.mutex.RUnlock()
  782. _ = client.Send("CAP REQ :" + requestedCaps)
  783. }
  784. case "NEW":
  785. {
  786. requests := make([]string, 0, len(capTokens))
  787. for _, token := range capTokens {
  788. for i := range supportedCaps {
  789. if supportedCaps[i] == token {
  790. requests = append(requests, token)
  791. }
  792. }
  793. }
  794. if len(requests) > 0 {
  795. _ = client.Send("CAP REQ :" + strings.Join(requests, " "))
  796. }
  797. }
  798. case "DEL":
  799. {
  800. for _, token := range capTokens {
  801. client.mutex.Lock()
  802. if client.capEnabled[token] {
  803. client.capEnabled[token] = false
  804. }
  805. client.mutex.Unlock()
  806. }
  807. }
  808. }
  809. }
  810. // User/host detection
  811. case "packet.352": // WHO reply
  812. {
  813. // Example args: test * ~irce 127.0.0.1 localhost.localnetwork Gissleh H :0 ...
  814. nick := event.Args[5]
  815. user := event.Args[2]
  816. host := event.Args[3]
  817. if nick == client.nick {
  818. client.mutex.Lock()
  819. client.user = user
  820. client.host = host
  821. client.mutex.Unlock()
  822. }
  823. }
  824. case "packet.chghost":
  825. {
  826. if event.Nick == client.nick {
  827. client.mutex.Lock()
  828. client.user = event.Args[1]
  829. client.host = event.Args[2]
  830. client.mutex.Unlock()
  831. }
  832. // This may be relevant in channels where the client resides.
  833. client.handleInTargets(event.Nick, event)
  834. }
  835. // Channel join/leave/mode handling
  836. case "packet.join":
  837. {
  838. var channel *Channel
  839. if event.Nick == client.nick {
  840. channel = &Channel{name: event.Arg(0), userlist: list.New(&client.isupport)}
  841. _, _ = client.AddTarget(channel)
  842. } else {
  843. channel = client.Channel(event.Arg(0))
  844. }
  845. client.handleInTarget(channel, event)
  846. }
  847. case "packet.part":
  848. {
  849. channel := client.Channel(event.Arg(0))
  850. if channel == nil {
  851. break
  852. }
  853. if event.Nick == client.nick {
  854. channel.parted = true
  855. _, _ = client.RemoveTarget(channel)
  856. } else {
  857. client.handleInTarget(channel, event)
  858. }
  859. }
  860. case "packet.kick":
  861. {
  862. channel := client.Channel(event.Arg(0))
  863. if channel == nil {
  864. break
  865. }
  866. if event.Arg(1) == client.nick {
  867. channel.parted = true
  868. _, _ = client.RemoveTarget(channel)
  869. } else {
  870. client.handleInTarget(channel, event)
  871. }
  872. }
  873. case "packet.quit":
  874. {
  875. client.handleInTargets(event.Nick, event)
  876. }
  877. case "packet.353": // NAMES
  878. {
  879. channel := client.Channel(event.Arg(2))
  880. if channel != nil {
  881. client.handleInTarget(channel, event)
  882. }
  883. }
  884. case "packet.366": // End of NAMES
  885. {
  886. channel := client.Channel(event.Arg(1))
  887. if channel != nil {
  888. client.handleInTarget(channel, event)
  889. }
  890. }
  891. case "packet.mode":
  892. {
  893. targetName := event.Arg(0)
  894. if client.isupport.IsChannel(targetName) {
  895. channel := client.Channel(targetName)
  896. if channel != nil {
  897. client.handleInTarget(channel, event)
  898. }
  899. }
  900. }
  901. // Message parsing
  902. case "packet.privmsg", "ctcp.action":
  903. {
  904. // Target the message
  905. target := Target(client.status)
  906. targetName := event.Arg(0)
  907. if targetName == client.nick {
  908. target := client.Target("query", targetName)
  909. if target == nil {
  910. query := &Query{user: list.User{
  911. Nick: event.Nick,
  912. User: event.User,
  913. Host: event.Host,
  914. }}
  915. id, _ := client.AddTarget(query)
  916. event.RenderTags["spawned"] = id
  917. target = query
  918. }
  919. } else {
  920. channel := client.Channel(targetName)
  921. if channel != nil {
  922. if user, ok := channel.UserList().User(event.Nick); ok {
  923. event.RenderTags["prefixedNick"] = user.PrefixedNick
  924. }
  925. target = channel
  926. } else {
  927. target = client.status
  928. }
  929. }
  930. client.handleInTarget(target, event)
  931. }
  932. case "packet.notice":
  933. {
  934. // Find channel target
  935. targetName := event.Arg(0)
  936. if client.isupport.IsChannel(targetName) {
  937. channel := client.Channel(targetName)
  938. if channel != nil {
  939. if user, ok := channel.UserList().User(event.Nick); ok {
  940. event.RenderTags["prefixedNick"] = user.PrefixedNick
  941. }
  942. client.handleInTarget(channel, event)
  943. }
  944. } else {
  945. // Try to target by mentioned channel name
  946. for _, token := range strings.Fields(event.Text) {
  947. if client.isupport.IsChannel(token) {
  948. channel := client.Channel(token)
  949. if channel == nil {
  950. continue
  951. }
  952. if user, ok := channel.UserList().User(event.Nick); ok {
  953. event.RenderTags["prefixedNick"] = user.PrefixedNick
  954. }
  955. client.handleInTarget(channel, event)
  956. break
  957. }
  958. }
  959. }
  960. // Otherwise, it belongs in the status target
  961. if len(event.targets) == 0 {
  962. client.status.Handle(event, client)
  963. client.handleInTarget(client.status, event)
  964. }
  965. }
  966. // account-notify
  967. case "packet.account":
  968. {
  969. client.handleInTargets(event.Nick, event)
  970. }
  971. // away-notify
  972. case "packet.away":
  973. {
  974. client.handleInTargets(event.Nick, event)
  975. }
  976. // Auto-rejoin
  977. case "packet.376", "packet.422":
  978. {
  979. client.mutex.RLock()
  980. channels := make([]string, 0, len(client.targets))
  981. rejoinEvent := NewEvent("info", "rejoin")
  982. for _, target := range client.targets {
  983. if channel, ok := target.(*Channel); ok {
  984. channels = append(channels, channel.Name())
  985. rejoinEvent.targets = append(rejoinEvent.targets, target)
  986. rejoinEvent.targetIds[target] = client.targetIds[target]
  987. }
  988. }
  989. client.mutex.RUnlock()
  990. if len(channels) > 0 {
  991. _ = client.Sendf("JOIN %s", strings.Join(channels, ","))
  992. client.EmitNonBlocking(rejoinEvent)
  993. }
  994. client.mutex.Lock()
  995. client.ready = true
  996. client.mutex.Unlock()
  997. client.EmitNonBlocking(NewEvent("hook", "ready"))
  998. }
  999. }
  1000. if len(event.targets) == 0 {
  1001. client.handleInTarget(client.status, event)
  1002. }
  1003. client.mutex.RLock()
  1004. clientHandlers := client.handlers
  1005. client.mutex.RUnlock()
  1006. for _, handler := range globalHandlers {
  1007. handler(event, client)
  1008. }
  1009. for _, handler := range clientHandlers {
  1010. handler(event, client)
  1011. }
  1012. }
  1013. func (client *Client) handleInTargets(nick string, event *Event) {
  1014. client.mutex.RLock()
  1015. for i := range client.targets {
  1016. switch target := client.targets[i].(type) {
  1017. case *Channel:
  1018. {
  1019. if nick != "" {
  1020. if _, ok := target.UserList().User(event.Nick); !ok {
  1021. continue
  1022. }
  1023. }
  1024. target.Handle(event, client)
  1025. event.targets = append(event.targets, target)
  1026. event.targetIds[target] = client.targetIds[target]
  1027. }
  1028. case *Query:
  1029. {
  1030. if target.user.Nick == nick {
  1031. target.Handle(event, client)
  1032. event.targets = append(event.targets, target)
  1033. event.targetIds[target] = client.targetIds[target]
  1034. }
  1035. }
  1036. case *Status:
  1037. {
  1038. if client.nick == event.Nick {
  1039. target.Handle(event, client)
  1040. event.targets = append(event.targets, target)
  1041. event.targetIds[target] = client.targetIds[target]
  1042. }
  1043. }
  1044. }
  1045. }
  1046. client.mutex.RUnlock()
  1047. }
  1048. func (client *Client) handleInTarget(target Target, event *Event) {
  1049. if target == nil {
  1050. return
  1051. }
  1052. client.mutex.RLock()
  1053. target.Handle(event, client)
  1054. event.targets = append(event.targets, target)
  1055. event.targetIds[target] = client.targetIds[target]
  1056. client.mutex.RUnlock()
  1057. }
  1058. func generateClientID() string {
  1059. bytes := make([]byte, 12)
  1060. _, err := rand.Read(bytes)
  1061. // Ugly fallback if crypto rand doesn't work.
  1062. if err != nil {
  1063. rng := mathRand.NewSource(time.Now().UnixNano())
  1064. result := strconv.FormatInt(rng.Int63(), 16)
  1065. for len(result) < 24 {
  1066. result += strconv.FormatInt(rng.Int63(), 16)
  1067. }
  1068. return result[:24]
  1069. }
  1070. binary.BigEndian.PutUint32(bytes[4:], uint32(time.Now().Unix()))
  1071. return hex.EncodeToString(bytes)
  1072. }