func (ctrl *Controller) terminal(ctx scope.Context, ch ssh.Channel) { defer ch.Close() lines := make(chan string) term := terminal.NewTerminal(ch, "> ") go func() { for ctx.Err() == nil { line, err := term.ReadLine() if err != nil { ctx.Terminate(err) return } lines <- line } }() for { var line string select { case <-ctx.Done(): return case line = <-lines: } cmd := parse(line) fmt.Printf("[control] > %v\n", cmd) switch cmd[0] { case "": continue case "quit": return case "shutdown": ctrl.ctx.Terminate(fmt.Errorf("shutdown initiated from console")) default: runCommand(ctx.Fork(), ctrl, cmd[0], term, cmd[1:]) } } }
// Starts a new bot, blocking until the bot finishes. func (bcfg *BotConfig) Start(ctx scope.Context, heimDialer proto.ConnectionDialer) error { forkedCtx := ctx.Fork() conn, err := RetryingConnectionDialer(heimDialer).Dial(forkedCtx, bcfg.RoomName) if err != nil { return err } sessions := make(map[string]*Session) bot := &Bot{ ctx: forkedCtx, conn: conn, sessions: sessions, nick: bcfg.Nick, } for _, cmd := range bcfg.Cmds { err := bot.NewSession(RetryingPluginDialer(&cmd)) if err != nil { // If we can't make a session we just failed at start up bot.Kill(err) return err } } go bot.forwardPacketLoop() _ = <-bot.ctx.Done() return bot.ctx.Err() }
func (et *EmailTracker) Send( ctx scope.Context, js jobs.JobService, templater *templates.Templater, deliverer emails.Deliverer, account proto.Account, to, templateName string, data interface{}) ( *emails.EmailRef, error) { if to == "" { to, _ = account.Email() } sf, err := snowflake.New() if err != nil { return nil, err } msgID := fmt.Sprintf("<%s@%s>", sf, deliverer.LocalName()) ref, err := emails.NewEmail(templater, msgID, to, templateName, data) if err != nil { return nil, err } ref.AccountID = account.ID() jq, err := js.GetQueue(ctx, jobs.EmailQueue) if err != nil { return nil, err } payload := &jobs.EmailJob{ AccountID: account.ID(), EmailID: ref.ID, } job, err := jq.AddAndClaim(ctx, jobs.EmailJobType, payload, "immediate", jobs.EmailJobOptions...) if err != nil { return nil, err } ref.JobID = job.ID et.m.Lock() if et.emailsByAccount == nil { et.emailsByAccount = map[snowflake.Snowflake][]*emails.EmailRef{} } et.emailsByAccount[account.ID()] = append(et.emailsByAccount[account.ID()], ref) et.m.Unlock() child := ctx.Fork() child.WaitGroup().Add(1) go job.Exec(child, func(ctx scope.Context) error { defer ctx.WaitGroup().Done() logging.Logger(ctx).Printf("delivering to %s\n", to) if err := deliverer.Deliver(ctx, ref); err != nil { return err } return nil }) return ref, nil }
func (d *RetryPluginDialer) Dial(ctx scope.Context) (proto.Plugin, error) { retry := &RetryPlugin{ ctx: ctx.Fork(), dialer: d.Dialer, } err := retry.start(nil) if err != nil { return nil, err } return retry, err }
func WSConnectionFactory(ctx scope.Context, roomName string) proto.Connection { return &Connection{ ctx: ctx.Fork(), testToClient: make(chan *hproto.Packet), clientToTest: make(chan *hproto.Packet), incoming: make(chan *hproto.Packet), outgoing: make(chan *hproto.Packet), } }
func (d *RetryConnectionDialer) Dial(ctx scope.Context, roomName string) (proto.Connection, error) { retry := &RetryConnection{ ctx: ctx.Fork(), dialer: d.Dialer, roomName: roomName, } err := retry.start(nil) if err != nil { return nil, err } return retry, nil }
func (d *CmdPluginDialer) Dial(ctx scope.Context) (proto.Plugin, error) { cmdPlugin := &CmdPlugin{ ctx: ctx.Fork(), cmd: d.Cmd, args: d.Args, } err := cmdPlugin.connect() if err != nil { proto.GetLogger(cmdPlugin.ctx).Errorf("Failed to connect CmdPlugin: %s", err) return nil, err } return cmdPlugin, nil }
func (d *WSConnectionDialer) Dial(ctx scope.Context, roomName string) (proto.Connection, error) { ws := WSConnection{ roomName: roomName, ctx: ctx.Fork(), incoming: make(chan *hproto.Packet, 8), outgoing: make(chan *hproto.Packet, 8)} dialer := websocket.Dialer{ HandshakeTimeout: 5 * time.Second, } url := fmt.Sprintf(d.UrlFormat, ws.roomName) proto.GetLogger(ws.ctx).Infof("Connecting to: %s", url) conn, _, err := dialer.Dial(url, nil) if err != nil { return nil, err } ws.conn = conn ws.start() return &ws, nil }
func ScanLoop(ctx scope.Context, c cluster.Cluster, pb *psql.Backend, interval time.Duration) { defer ctx.WaitGroup().Done() errCount := 0 for { t := time.After(interval) select { case <-ctx.Done(): return case <-t: if err := scan(ctx.Fork(), c, pb); err != nil { errCount++ fmt.Printf("scan error [%d/%d]: %s", errCount, maxErrors, err) if errCount > maxErrors { fmt.Printf("maximum scan errors exceeded, terminating\n") ctx.Terminate(fmt.Errorf("maximum scan errors exceeded")) return } continue } errCount = 0 } } }
func (et *EmailTracker) Send( ctx scope.Context, js jobs.JobService, templater *templates.Templater, deliverer emails.Deliverer, account proto.Account, templateName string, data interface{}) ( *emails.EmailRef, error) { // choose a Message-ID sf, err := snowflake.New() if err != nil { return nil, err } domain := "heim" if deliverer != nil { domain = deliverer.LocalName() } msgID := fmt.Sprintf("<%s@%s>", sf, domain) // choose an address to send to to := "" /* requireVerifiedAddress := true switch templateName { case proto.WelcomeEmail, proto.RoomInvitationWelcomeEmail, proto.PasswordResetEmail: requireVerifiedAddress = false } */ for _, pid := range account.PersonalIdentities() { if pid.Namespace() == "email" { /* if !pid.Verified() && requireVerifiedAddress { continue } */ to = pid.ID() break } } if to == "" { fmt.Printf("no email address to deliver to\n") return nil, fmt.Errorf("account has no email address to deliver %s to", templateName) } // construct the email ref, err := emails.NewEmail(templater, msgID, to, templateName, data) if err != nil { return nil, err } ref.AccountID = account.ID() // get underlying JobQueue so we can add-and-claim in the same transaction as the email insert abstractQueue, err := js.GetQueue(ctx, jobs.EmailQueue) if err != nil { return nil, err } jq := abstractQueue.(*JobQueueBinding) t, err := et.Backend.DbMap.Begin() if err != nil { return nil, err } // insert job first, so we know what JobID to associate with the email when we insert it payload := &jobs.EmailJob{ AccountID: account.ID(), EmailID: ref.ID, } job, err := jq.addAndClaim(ctx, t, jobs.EmailJobType, payload, "immediate", jobs.EmailJobOptions...) if err != nil { rollback(ctx, t) return nil, err } ref.JobID = job.ID // insert the email var email Email email.FromBackend(ref) if err := t.Insert(&email); err != nil { rollback(ctx, t) return nil, err } // finalize and spin off first delivery attempt if err := t.Commit(); err != nil { return nil, err } child := ctx.Fork() child.WaitGroup().Add(1) go job.Exec(child, func(ctx scope.Context) error { defer ctx.WaitGroup().Done() logging.Logger(ctx).Printf("delivering to %s\n", to) if deliverer == nil { return fmt.Errorf("deliverer not configured") } if err := deliverer.Deliver(ctx, ref); err != nil { return err } if _, err := et.Backend.DbMap.Exec("UPDATE email SET delivered = $2 WHERE id = $1", ref.ID, ref.Delivered); err != nil { // Even if we fail to mark the email as delivered, don't return an // error so the job still gets completed. We wouldn't want to spam // someone just because of a DB issue. logging.Logger(ctx).Printf("error marking email %s/%s as delivered: %s", account.ID(), ref.ID, err) } return nil }) return ref, nil }
func NewBot(ctx scope.Context) *Bot { return &Bot{ ctx: ctx.Fork(), rooms: make(map[string]proto.Room), } }
func (et *EmailTracker) Send( ctx scope.Context, js jobs.JobService, templater *templates.Templater, deliverer emails.Deliverer, account proto.Account, templateName string, data interface{}) ( *emails.EmailRef, error) { sf, err := snowflake.New() if err != nil { return nil, err } msgID := fmt.Sprintf("<%s@%s>", sf, deliverer.LocalName()) to := "" /* requireVerifiedAddress := true switch templateName { case proto.WelcomeEmail, proto.RoomInvitationWelcomeEmail, proto.PasswordResetEmail: requireVerifiedAddress = false } */ for _, pid := range account.PersonalIdentities() { if pid.Namespace() == "email" { /* if !pid.Verified() && requireVerifiedAddress { continue } */ to = pid.ID() break } } if to == "" { fmt.Printf("no email address to deliver to\n") return nil, fmt.Errorf("account has no email address to deliver %s to", templateName) } ref, err := emails.NewEmail(templater, msgID, to, templateName, data) if err != nil { return nil, err } ref.AccountID = account.ID() jq, err := js.GetQueue(ctx, jobs.EmailQueue) if err != nil { return nil, err } payload := &jobs.EmailJob{ AccountID: account.ID(), EmailID: ref.ID, } job, err := jq.AddAndClaim(ctx, jobs.EmailJobType, payload, "immediate", jobs.EmailJobOptions...) if err != nil { return nil, err } ref.JobID = job.ID et.m.Lock() if et.emailsByAccount == nil { et.emailsByAccount = map[snowflake.Snowflake][]*emails.EmailRef{} } et.emailsByAccount[account.ID()] = append(et.emailsByAccount[account.ID()], ref) et.m.Unlock() child := ctx.Fork() child.WaitGroup().Add(1) go job.Exec(child, func(ctx scope.Context) error { defer ctx.WaitGroup().Done() logging.Logger(ctx).Printf("delivering to %s\n", to) if err := deliverer.Deliver(ctx, ref); err != nil { return err } return nil }) return ref, nil }
// NewMockDialer creates a new MockDialer. func NewMockDialer(ctx scope.Context) *MockDialer { return &MockDialer{ ctx: ctx.Fork(), } }