source: code/trunk/user.go@ 564

Last change on this file since 564 was 563, checked in by contact, 4 years ago

Allow admins to broadcast message to all bouncer users

Typically done via:

/notice $<bouncer> <message>

Or, for a connection not bound to a specific network:

/notice $* <message>

The message is broadcast as BouncerServ, because that's the only
user that can be trusted to belong to the bouncer by users. Any
other prefix would conflict with the upstream network.

File size: 20.0 KB
RevLine 
[101]1package soju
2
3import (
[385]4 "crypto/sha256"
5 "encoding/binary"
[395]6 "encoding/hex"
[218]7 "fmt"
[101]8 "time"
[103]9
10 "gopkg.in/irc.v3"
[101]11)
12
[165]13type event interface{}
14
15type eventUpstreamMessage struct {
[103]16 msg *irc.Message
17 uc *upstreamConn
18}
19
[218]20type eventUpstreamConnectionError struct {
21 net *network
22 err error
23}
24
[196]25type eventUpstreamConnected struct {
26 uc *upstreamConn
27}
28
[179]29type eventUpstreamDisconnected struct {
30 uc *upstreamConn
31}
32
[218]33type eventUpstreamError struct {
34 uc *upstreamConn
35 err error
36}
37
[165]38type eventDownstreamMessage struct {
[103]39 msg *irc.Message
40 dc *downstreamConn
41}
42
[166]43type eventDownstreamConnected struct {
44 dc *downstreamConn
45}
46
[167]47type eventDownstreamDisconnected struct {
48 dc *downstreamConn
49}
50
[435]51type eventChannelDetach struct {
52 uc *upstreamConn
53 name string
54}
55
[563]56type eventBroadcast struct {
57 msg *irc.Message
58}
59
[376]60type eventStop struct{}
61
[480]62type deliveredClientMap map[string]string // client name -> msg ID
63
[485]64type deliveredStore struct {
65 m deliveredCasemapMap
66}
67
68func newDeliveredStore() deliveredStore {
69 return deliveredStore{deliveredCasemapMap{newCasemapMap(0)}}
70}
71
72func (ds deliveredStore) HasTarget(target string) bool {
73 return ds.m.Value(target) != nil
74}
75
76func (ds deliveredStore) LoadID(target, clientName string) string {
77 clients := ds.m.Value(target)
78 if clients == nil {
79 return ""
80 }
81 return clients[clientName]
82}
83
84func (ds deliveredStore) StoreID(target, clientName, msgID string) {
85 clients := ds.m.Value(target)
86 if clients == nil {
87 clients = make(deliveredClientMap)
88 ds.m.SetValue(target, clients)
89 }
90 clients[clientName] = msgID
91}
92
93func (ds deliveredStore) ForEachTarget(f func(target string)) {
94 for _, entry := range ds.m.innerMap {
95 f(entry.originalKey)
96 }
97}
98
[489]99func (ds deliveredStore) ForEachClient(f func(clientName string)) {
100 clients := make(map[string]struct{})
101 for _, entry := range ds.m.innerMap {
102 delivered := entry.value.(deliveredClientMap)
103 for clientName := range delivered {
104 clients[clientName] = struct{}{}
105 }
106 }
107
108 for clientName := range clients {
109 f(clientName)
110 }
111}
112
[101]113type network struct {
114 Network
[202]115 user *user
[501]116 logger Logger
[202]117 stopped chan struct{}
[131]118
[482]119 conn *upstreamConn
120 channels channelCasemapMap
[485]121 delivered deliveredStore
[482]122 lastError error
123 casemap casemapping
[101]124}
125
[267]126func newNetwork(user *user, record *Network, channels []Channel) *network {
[501]127 logger := &prefixLogger{user.logger, fmt.Sprintf("network %q: ", record.GetName())}
128
[478]129 m := channelCasemapMap{newCasemapMap(0)}
[267]130 for _, ch := range channels {
[283]131 ch := ch
[478]132 m.SetValue(ch.Name, &ch)
[267]133 }
134
[101]135 return &network{
[482]136 Network: *record,
137 user: user,
[501]138 logger: logger,
[482]139 stopped: make(chan struct{}),
140 channels: m,
[485]141 delivered: newDeliveredStore(),
[482]142 casemap: casemapRFC1459,
[101]143 }
144}
145
[218]146func (net *network) forEachDownstream(f func(*downstreamConn)) {
147 net.user.forEachDownstream(func(dc *downstreamConn) {
[532]148 if dc.network == nil && dc.caps["soju.im/bouncer-networks"] {
149 return
150 }
[218]151 if dc.network != nil && dc.network != net {
152 return
153 }
154 f(dc)
155 })
156}
157
[311]158func (net *network) isStopped() bool {
159 select {
160 case <-net.stopped:
161 return true
162 default:
163 return false
164 }
165}
166
[385]167func userIdent(u *User) string {
168 // The ident is a string we will send to upstream servers in clear-text.
169 // For privacy reasons, make sure it doesn't expose any meaningful user
170 // metadata. We just use the base64-encoded hashed ID, so that people don't
171 // start relying on the string being an integer or following a pattern.
172 var b [64]byte
173 binary.LittleEndian.PutUint64(b[:], uint64(u.ID))
174 h := sha256.Sum256(b[:])
[395]175 return hex.EncodeToString(h[:16])
[385]176}
177
[101]178func (net *network) run() {
[542]179 if !net.Enabled {
180 return
181 }
182
[101]183 var lastTry time.Time
184 for {
[311]185 if net.isStopped() {
[202]186 return
187 }
188
[398]189 if dur := time.Now().Sub(lastTry); dur < retryConnectDelay {
190 delay := retryConnectDelay - dur
[501]191 net.logger.Printf("waiting %v before trying to reconnect to %q", delay.Truncate(time.Second), net.Addr)
[101]192 time.Sleep(delay)
193 }
194 lastTry = time.Now()
195
196 uc, err := connectToUpstream(net)
197 if err != nil {
[501]198 net.logger.Printf("failed to connect to upstream server %q: %v", net.Addr, err)
[218]199 net.user.events <- eventUpstreamConnectionError{net, fmt.Errorf("failed to connect: %v", err)}
[101]200 continue
201 }
202
[385]203 if net.user.srv.Identd != nil {
204 net.user.srv.Identd.Store(uc.RemoteAddr().String(), uc.LocalAddr().String(), userIdent(&net.user.User))
205 }
206
[101]207 uc.register()
[197]208 if err := uc.runUntilRegistered(); err != nil {
[399]209 text := err.Error()
210 if regErr, ok := err.(registrationError); ok {
211 text = string(regErr)
212 }
213 uc.logger.Printf("failed to register: %v", text)
214 net.user.events <- eventUpstreamConnectionError{net, fmt.Errorf("failed to register: %v", text)}
[197]215 uc.Close()
216 continue
217 }
[101]218
[311]219 // TODO: this is racy with net.stopped. If the network is stopped
220 // before the user goroutine receives eventUpstreamConnected, the
221 // connection won't be closed.
[196]222 net.user.events <- eventUpstreamConnected{uc}
[165]223 if err := uc.readMessages(net.user.events); err != nil {
[101]224 uc.logger.Printf("failed to handle messages: %v", err)
[218]225 net.user.events <- eventUpstreamError{uc, fmt.Errorf("failed to handle messages: %v", err)}
[101]226 }
227 uc.Close()
[179]228 net.user.events <- eventUpstreamDisconnected{uc}
[385]229
230 if net.user.srv.Identd != nil {
231 net.user.srv.Identd.Delete(uc.RemoteAddr().String(), uc.LocalAddr().String())
232 }
[101]233 }
234}
235
[309]236func (net *network) stop() {
[311]237 if !net.isStopped() {
[202]238 close(net.stopped)
239 }
240
[279]241 if net.conn != nil {
242 net.conn.Close()
[202]243 }
244}
245
[435]246func (net *network) detach(ch *Channel) {
247 if ch.Detached {
248 return
[267]249 }
[497]250
[501]251 net.logger.Printf("detaching channel %q", ch.Name)
[435]252
[497]253 ch.Detached = true
254
255 if net.user.msgStore != nil {
256 nameCM := net.casemap(ch.Name)
257 lastID, err := net.user.msgStore.LastMsgID(net, nameCM, time.Now())
258 if err != nil {
[501]259 net.logger.Printf("failed to get last message ID for channel %q: %v", ch.Name, err)
[497]260 }
261 ch.DetachedInternalMsgID = lastID
262 }
263
[435]264 if net.conn != nil {
[478]265 uch := net.conn.channels.Value(ch.Name)
266 if uch != nil {
[435]267 uch.updateAutoDetach(0)
268 }
[222]269 }
[284]270
[435]271 net.forEachDownstream(func(dc *downstreamConn) {
272 dc.SendMessage(&irc.Message{
273 Prefix: dc.prefix(),
274 Command: "PART",
275 Params: []string{dc.marshalEntity(net, ch.Name), "Detach"},
276 })
277 })
278}
[284]279
[435]280func (net *network) attach(ch *Channel) {
281 if !ch.Detached {
282 return
283 }
[497]284
[501]285 net.logger.Printf("attaching channel %q", ch.Name)
[284]286
[497]287 detachedMsgID := ch.DetachedInternalMsgID
288 ch.Detached = false
289 ch.DetachedInternalMsgID = ""
290
[435]291 var uch *upstreamChannel
292 if net.conn != nil {
[478]293 uch = net.conn.channels.Value(ch.Name)
[284]294
[435]295 net.conn.updateChannelAutoDetach(ch.Name)
296 }
[284]297
[435]298 net.forEachDownstream(func(dc *downstreamConn) {
299 dc.SendMessage(&irc.Message{
300 Prefix: dc.prefix(),
301 Command: "JOIN",
302 Params: []string{dc.marshalEntity(net, ch.Name)},
303 })
304
305 if uch != nil {
306 forwardChannel(dc, uch)
[284]307 }
308
[497]309 if detachedMsgID != "" {
310 dc.sendTargetBacklog(net, ch.Name, detachedMsgID)
[495]311 }
[435]312 })
[222]313}
314
315func (net *network) deleteChannel(name string) error {
[478]316 ch := net.channels.Value(name)
317 if ch == nil {
[416]318 return fmt.Errorf("unknown channel %q", name)
319 }
[435]320 if net.conn != nil {
[478]321 uch := net.conn.channels.Value(ch.Name)
322 if uch != nil {
[435]323 uch.updateAutoDetach(0)
324 }
325 }
326
[416]327 if err := net.user.srv.db.DeleteChannel(ch.ID); err != nil {
[267]328 return err
329 }
[478]330 net.channels.Delete(name)
[267]331 return nil
[222]332}
333
[478]334func (net *network) updateCasemapping(newCasemap casemapping) {
335 net.casemap = newCasemap
336 net.channels.SetCasemapping(newCasemap)
[485]337 net.delivered.m.SetCasemapping(newCasemap)
[478]338 if net.conn != nil {
339 net.conn.channels.SetCasemapping(newCasemap)
340 for _, entry := range net.conn.channels.innerMap {
341 uch := entry.value.(*upstreamChannel)
342 uch.Members.SetCasemapping(newCasemap)
343 }
344 }
345}
346
[489]347func (net *network) storeClientDeliveryReceipts(clientName string) {
348 if !net.user.hasPersistentMsgStore() {
349 return
350 }
351
352 var receipts []DeliveryReceipt
353 net.delivered.ForEachTarget(func(target string) {
354 msgID := net.delivered.LoadID(target, clientName)
355 if msgID == "" {
356 return
357 }
358 receipts = append(receipts, DeliveryReceipt{
359 Target: target,
360 InternalMsgID: msgID,
361 })
362 })
363
364 if err := net.user.srv.db.StoreClientDeliveryReceipts(net.ID, clientName, receipts); err != nil {
[501]365 net.logger.Printf("failed to store delivery receipts for client %q: %v", clientName, err)
[489]366 }
367}
368
[499]369func (net *network) isHighlight(msg *irc.Message) bool {
370 if msg.Command != "PRIVMSG" && msg.Command != "NOTICE" {
371 return false
372 }
373
374 text := msg.Params[1]
375
376 nick := net.Nick
377 if net.conn != nil {
378 nick = net.conn.nick
379 }
380
381 // TODO: use case-mapping aware comparison here
382 return msg.Prefix.Name != nick && isHighlight(text, nick)
383}
384
385func (net *network) detachedMessageNeedsRelay(ch *Channel, msg *irc.Message) bool {
386 highlight := net.isHighlight(msg)
387 return ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight)
388}
389
[101]390type user struct {
391 User
[493]392 srv *Server
393 logger Logger
[101]394
[165]395 events chan event
[377]396 done chan struct{}
[103]397
[101]398 networks []*network
399 downstreamConns []*downstreamConn
[439]400 msgStore messageStore
[177]401
402 // LIST commands in progress
[179]403 pendingLISTs []pendingLIST
[101]404}
405
[177]406type pendingLIST struct {
407 downstreamID uint64
408 // list of per-upstream LIST commands not yet sent or completed
409 pendingCommands map[int64]*irc.Message
410}
411
[101]412func newUser(srv *Server, record *User) *user {
[493]413 logger := &prefixLogger{srv.Logger, fmt.Sprintf("user %q: ", record.Username)}
414
[439]415 var msgStore messageStore
[423]416 if srv.LogPath != "" {
[439]417 msgStore = newFSMessageStore(srv.LogPath, record.Username)
[442]418 } else {
419 msgStore = newMemoryMessageStore()
[423]420 }
421
[101]422 return &user{
[489]423 User: *record,
424 srv: srv,
[493]425 logger: logger,
[489]426 events: make(chan event, 64),
427 done: make(chan struct{}),
428 msgStore: msgStore,
[101]429 }
430}
431
432func (u *user) forEachNetwork(f func(*network)) {
433 for _, network := range u.networks {
434 f(network)
435 }
436}
437
438func (u *user) forEachUpstream(f func(uc *upstreamConn)) {
439 for _, network := range u.networks {
[279]440 if network.conn == nil {
[101]441 continue
442 }
[279]443 f(network.conn)
[101]444 }
445}
446
447func (u *user) forEachDownstream(f func(dc *downstreamConn)) {
448 for _, dc := range u.downstreamConns {
449 f(dc)
450 }
451}
452
453func (u *user) getNetwork(name string) *network {
454 for _, network := range u.networks {
455 if network.Addr == name {
456 return network
457 }
[201]458 if network.Name != "" && network.Name == name {
459 return network
460 }
[101]461 }
462 return nil
463}
464
[313]465func (u *user) getNetworkByID(id int64) *network {
466 for _, net := range u.networks {
467 if net.ID == id {
468 return net
469 }
470 }
471 return nil
472}
473
[101]474func (u *user) run() {
[423]475 defer func() {
476 if u.msgStore != nil {
477 if err := u.msgStore.Close(); err != nil {
[493]478 u.logger.Printf("failed to close message store for user %q: %v", u.Username, err)
[423]479 }
480 }
481 close(u.done)
482 }()
[377]483
[421]484 networks, err := u.srv.db.ListNetworks(u.ID)
[101]485 if err != nil {
[493]486 u.logger.Printf("failed to list networks for user %q: %v", u.Username, err)
[101]487 return
488 }
489
490 for _, record := range networks {
[283]491 record := record
[267]492 channels, err := u.srv.db.ListChannels(record.ID)
493 if err != nil {
[493]494 u.logger.Printf("failed to list channels for user %q, network %q: %v", u.Username, record.GetName(), err)
[359]495 continue
[267]496 }
497
498 network := newNetwork(u, &record, channels)
[101]499 u.networks = append(u.networks, network)
500
[489]501 if u.hasPersistentMsgStore() {
502 receipts, err := u.srv.db.ListDeliveryReceipts(record.ID)
503 if err != nil {
[493]504 u.logger.Printf("failed to load delivery receipts for user %q, network %q: %v", u.Username, network.GetName(), err)
[489]505 return
506 }
507
508 for _, rcpt := range receipts {
509 network.delivered.StoreID(rcpt.Target, rcpt.Client, rcpt.InternalMsgID)
510 }
511 }
512
[101]513 go network.run()
514 }
[103]515
[165]516 for e := range u.events {
517 switch e := e.(type) {
[196]518 case eventUpstreamConnected:
[198]519 uc := e.uc
[199]520
521 uc.network.conn = uc
522
[198]523 uc.updateAway()
[218]524
[532]525 netIDStr := fmt.Sprintf("%v", uc.network.ID)
[218]526 uc.forEachDownstream(func(dc *downstreamConn) {
[276]527 dc.updateSupportedCaps()
[296]528
[543]529 if !dc.caps["soju.im/bouncer-networks"] {
530 sendServiceNOTICE(dc, fmt.Sprintf("connected to %s", uc.network.GetName()))
531 }
532
533 dc.updateNick()
534 dc.updateRealname()
535 })
536 u.forEachDownstream(func(dc *downstreamConn) {
[535]537 if dc.caps["soju.im/bouncer-networks-notify"] {
[532]538 dc.SendMessage(&irc.Message{
539 Prefix: dc.srv.prefix(),
540 Command: "BOUNCER",
[544]541 Params: []string{"NETWORK", netIDStr, "state=connected"},
[532]542 })
543 }
[218]544 })
545 uc.network.lastError = nil
[179]546 case eventUpstreamDisconnected:
[313]547 u.handleUpstreamDisconnected(e.uc)
548 case eventUpstreamConnectionError:
549 net := e.net
[199]550
[313]551 stopped := false
552 select {
553 case <-net.stopped:
554 stopped = true
555 default:
[179]556 }
[199]557
[313]558 if !stopped && (net.lastError == nil || net.lastError.Error() != e.err.Error()) {
[218]559 net.forEachDownstream(func(dc *downstreamConn) {
[223]560 sendServiceNOTICE(dc, fmt.Sprintf("failed connecting/registering to %s: %v", net.GetName(), e.err))
[218]561 })
562 }
563 net.lastError = e.err
564 case eventUpstreamError:
565 uc := e.uc
566
567 uc.forEachDownstream(func(dc *downstreamConn) {
[223]568 sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s: %v", uc.network.GetName(), e.err))
[218]569 })
570 uc.network.lastError = e.err
[165]571 case eventUpstreamMessage:
572 msg, uc := e.msg, e.uc
[175]573 if uc.isClosed() {
[133]574 uc.logger.Printf("ignoring message on closed connection: %v", msg)
575 break
576 }
[103]577 if err := uc.handleMessage(msg); err != nil {
578 uc.logger.Printf("failed to handle message %q: %v", msg, err)
579 }
[435]580 case eventChannelDetach:
581 uc, name := e.uc, e.name
[478]582 c := uc.network.channels.Value(name)
583 if c == nil || c.Detached {
[435]584 continue
585 }
586 uc.network.detach(c)
587 if err := uc.srv.db.StoreChannel(uc.network.ID, c); err != nil {
[493]588 u.logger.Printf("failed to store updated detached channel %q: %v", c.Name, err)
[435]589 }
[166]590 case eventDownstreamConnected:
591 dc := e.dc
[168]592
593 if err := dc.welcome(); err != nil {
594 dc.logger.Printf("failed to handle new registered connection: %v", err)
595 break
596 }
597
[166]598 u.downstreamConns = append(u.downstreamConns, dc)
[198]599
[467]600 dc.forEachNetwork(func(network *network) {
601 if network.lastError != nil {
602 sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s: %v", network.GetName(), network.lastError))
603 }
604 })
605
[198]606 u.forEachUpstream(func(uc *upstreamConn) {
607 uc.updateAway()
608 })
[167]609 case eventDownstreamDisconnected:
610 dc := e.dc
[204]611
[167]612 for i := range u.downstreamConns {
613 if u.downstreamConns[i] == dc {
614 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
615 break
616 }
617 }
[198]618
[489]619 dc.forEachNetwork(func(net *network) {
620 net.storeClientDeliveryReceipts(dc.clientName)
621 })
622
[198]623 u.forEachUpstream(func(uc *upstreamConn) {
624 uc.updateAway()
625 })
[165]626 case eventDownstreamMessage:
627 msg, dc := e.msg, e.dc
[133]628 if dc.isClosed() {
629 dc.logger.Printf("ignoring message on closed connection: %v", msg)
630 break
631 }
[103]632 err := dc.handleMessage(msg)
633 if ircErr, ok := err.(ircError); ok {
634 ircErr.Message.Prefix = dc.srv.prefix()
635 dc.SendMessage(ircErr.Message)
636 } else if err != nil {
637 dc.logger.Printf("failed to handle message %q: %v", msg, err)
638 dc.Close()
639 }
[563]640 case eventBroadcast:
641 msg := e.msg
642 u.forEachDownstream(func(dc *downstreamConn) {
643 dc.SendMessage(msg)
644 })
[376]645 case eventStop:
646 u.forEachDownstream(func(dc *downstreamConn) {
647 dc.Close()
648 })
649 for _, n := range u.networks {
650 n.stop()
[489]651
652 n.delivered.ForEachClient(func(clientName string) {
653 n.storeClientDeliveryReceipts(clientName)
654 })
[376]655 }
656 return
[165]657 default:
[494]658 panic(fmt.Sprintf("received unknown event type: %T", e))
[103]659 }
660 }
[101]661}
662
[313]663func (u *user) handleUpstreamDisconnected(uc *upstreamConn) {
664 uc.network.conn = nil
665
666 uc.endPendingLISTs(true)
667
[478]668 for _, entry := range uc.channels.innerMap {
669 uch := entry.value.(*upstreamChannel)
[435]670 uch.updateAutoDetach(0)
671 }
672
[532]673 netIDStr := fmt.Sprintf("%v", uc.network.ID)
[313]674 uc.forEachDownstream(func(dc *downstreamConn) {
675 dc.updateSupportedCaps()
[543]676 })
677 u.forEachDownstream(func(dc *downstreamConn) {
[535]678 if dc.caps["soju.im/bouncer-networks-notify"] {
[532]679 dc.SendMessage(&irc.Message{
680 Prefix: dc.srv.prefix(),
681 Command: "BOUNCER",
[544]682 Params: []string{"NETWORK", netIDStr, "state=disconnected"},
[532]683 })
684 }
[313]685 })
686
687 if uc.network.lastError == nil {
688 uc.forEachDownstream(func(dc *downstreamConn) {
[532]689 if !dc.caps["soju.im/bouncer-networks"] {
690 sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s", uc.network.GetName()))
691 }
[313]692 })
693 }
694}
695
696func (u *user) addNetwork(network *network) {
697 u.networks = append(u.networks, network)
698 go network.run()
699}
700
701func (u *user) removeNetwork(network *network) {
702 network.stop()
703
704 u.forEachDownstream(func(dc *downstreamConn) {
705 if dc.network != nil && dc.network == network {
706 dc.Close()
707 }
708 })
709
710 for i, net := range u.networks {
711 if net == network {
712 u.networks = append(u.networks[:i], u.networks[i+1:]...)
713 return
714 }
715 }
716
717 panic("tried to remove a non-existing network")
718}
719
[500]720func (u *user) checkNetwork(record *Network) error {
721 for _, net := range u.networks {
722 if net.GetName() == record.GetName() && net.ID != record.ID {
723 return fmt.Errorf("a network with the name %q already exists", record.GetName())
724 }
725 }
726 return nil
727}
728
[313]729func (u *user) createNetwork(record *Network) (*network, error) {
730 if record.ID != 0 {
[144]731 panic("tried creating an already-existing network")
732 }
733
[500]734 if err := u.checkNetwork(record); err != nil {
735 return nil, err
736 }
737
[313]738 network := newNetwork(u, record, nil)
[421]739 err := u.srv.db.StoreNetwork(u.ID, &network.Network)
[101]740 if err != nil {
741 return nil, err
742 }
[144]743
[313]744 u.addNetwork(network)
[144]745
[532]746 idStr := fmt.Sprintf("%v", network.ID)
[535]747 attrs := getNetworkAttrs(network)
[532]748 u.forEachDownstream(func(dc *downstreamConn) {
[535]749 if dc.caps["soju.im/bouncer-networks-notify"] {
[532]750 dc.SendMessage(&irc.Message{
751 Prefix: dc.srv.prefix(),
752 Command: "BOUNCER",
[535]753 Params: []string{"NETWORK", idStr, attrs.String()},
[532]754 })
755 }
756 })
757
[101]758 return network, nil
759}
[202]760
[313]761func (u *user) updateNetwork(record *Network) (*network, error) {
762 if record.ID == 0 {
763 panic("tried updating a new network")
764 }
[202]765
[500]766 if err := u.checkNetwork(record); err != nil {
767 return nil, err
768 }
769
[313]770 network := u.getNetworkByID(record.ID)
771 if network == nil {
772 panic("tried updating a non-existing network")
773 }
774
[421]775 if err := u.srv.db.StoreNetwork(u.ID, record); err != nil {
[313]776 return nil, err
777 }
778
779 // Most network changes require us to re-connect to the upstream server
780
[478]781 channels := make([]Channel, 0, network.channels.Len())
782 for _, entry := range network.channels.innerMap {
783 ch := entry.value.(*Channel)
[313]784 channels = append(channels, *ch)
785 }
786
787 updatedNetwork := newNetwork(u, record, channels)
788
789 // If we're currently connected, disconnect and perform the necessary
790 // bookkeeping
791 if network.conn != nil {
792 network.stop()
793 // Note: this will set network.conn to nil
794 u.handleUpstreamDisconnected(network.conn)
795 }
796
797 // Patch downstream connections to use our fresh updated network
798 u.forEachDownstream(func(dc *downstreamConn) {
799 if dc.network != nil && dc.network == network {
800 dc.network = updatedNetwork
[202]801 }
[313]802 })
[202]803
[313]804 // We need to remove the network after patching downstream connections,
805 // otherwise they'll get closed
806 u.removeNetwork(network)
[202]807
[313]808 // This will re-connect to the upstream server
809 u.addNetwork(updatedNetwork)
810
[535]811 // TODO: only broadcast attributes that have changed
812 idStr := fmt.Sprintf("%v", updatedNetwork.ID)
813 attrs := getNetworkAttrs(updatedNetwork)
814 u.forEachDownstream(func(dc *downstreamConn) {
815 if dc.caps["soju.im/bouncer-networks-notify"] {
816 dc.SendMessage(&irc.Message{
817 Prefix: dc.srv.prefix(),
818 Command: "BOUNCER",
819 Params: []string{"NETWORK", idStr, attrs.String()},
820 })
821 }
822 })
[532]823
[313]824 return updatedNetwork, nil
825}
826
827func (u *user) deleteNetwork(id int64) error {
828 network := u.getNetworkByID(id)
829 if network == nil {
830 panic("tried deleting a non-existing network")
[202]831 }
832
[313]833 if err := u.srv.db.DeleteNetwork(network.ID); err != nil {
834 return err
835 }
836
837 u.removeNetwork(network)
[532]838
839 idStr := fmt.Sprintf("%v", network.ID)
840 u.forEachDownstream(func(dc *downstreamConn) {
[535]841 if dc.caps["soju.im/bouncer-networks-notify"] {
[532]842 dc.SendMessage(&irc.Message{
843 Prefix: dc.srv.prefix(),
844 Command: "BOUNCER",
845 Params: []string{"NETWORK", idStr, "*"},
846 })
847 }
848 })
849
[313]850 return nil
[202]851}
[252]852
853func (u *user) updatePassword(hashed string) error {
854 u.User.Password = hashed
[324]855 return u.srv.db.StoreUser(&u.User)
[252]856}
[376]857
858func (u *user) stop() {
859 u.events <- eventStop{}
[377]860 <-u.done
[376]861}
[489]862
863func (u *user) hasPersistentMsgStore() bool {
864 if u.msgStore == nil {
865 return false
866 }
867 _, isMem := u.msgStore.(*memoryMessageStore)
868 return !isMem
869}
Note: See TracBrowser for help on using the repository browser.