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.

1382 lines
32 KiB

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