func (u *gtkUI) buildStaticAccountsMenu(submenu *gtk.Menu) { connectAutomaticallyItem, _ := gtk.CheckMenuItemNewWithMnemonic(i18n.Local("Connect On _Startup")) u.config.WhenLoaded(func(a *config.ApplicationConfig) { connectAutomaticallyItem.SetActive(a.ConnectAutomatically) }) connectAutomaticallyItem.Connect("activate", func() { u.setConnectAllAutomatically(connectAutomaticallyItem.GetActive()) }) submenu.Append(connectAutomaticallyItem) connectAllMenu, _ := gtk.MenuItemNewWithMnemonic(i18n.Local("_Connect All")) connectAllMenu.Connect("activate", func() { u.connectAllAutomatics(true) }) submenu.Append(connectAllMenu) sep2, _ := gtk.SeparatorMenuItemNew() submenu.Append(sep2) addAccMenu, _ := gtk.MenuItemNewWithMnemonic(i18n.Local("_Add...")) addAccMenu.Connect("activate", u.showAddAccountWindow) submenu.Append(addAccMenu) importMenu, _ := gtk.MenuItemNewWithMnemonic(i18n.Local("_Import...")) importMenu.Connect("activate", u.runImporter) submenu.Append(importMenu) registerAccMenu, _ := gtk.MenuItemNewWithMnemonic(i18n.Local("_Register...")) registerAccMenu.Connect("activate", u.showServerSelectionWindow) submenu.Append(registerAccMenu) }
func (u *gtkUI) handlePeerEvent(ev events.Peer) { identityWarning := func(cv conversationView) { cv.updateSecurityWarning() cv.showIdentityVerificationWarning(u) } switch ev.Type { case events.IQReceived: //TODO log.Printf("received iq: %v\n", ev.From) case events.OTREnded: peer := ev.From account := u.findAccountForSession(ev.Session) convWindowNowOrLater(account, peer, func(cv conversationView) { cv.displayNotification(i18n.Local("Private conversation lost.")) cv.updateSecurityWarning() }) case events.OTRNewKeys: peer := ev.From account := u.findAccountForSession(ev.Session) convWindowNowOrLater(account, peer, func(cv conversationView) { cv.displayNotificationVerifiedOrNot(i18n.Local("Private conversation started."), i18n.Local("Unverified conversation started.")) identityWarning(cv) }) case events.OTRRenewedKeys: peer := ev.From account := u.findAccountForSession(ev.Session) convWindowNowOrLater(account, peer, func(cv conversationView) { cv.displayNotificationVerifiedOrNot(i18n.Local("Successfully refreshed the private conversation."), i18n.Local("Successfully refreshed the unverified private conversation.")) identityWarning(cv) }) case events.SubscriptionRequest: confirmDialog := authorizePresenceSubscriptionDialog(&u.window.Window, ev.From) doInUIThread(func() { responseType := gtk.ResponseType(confirmDialog.Run()) switch responseType { case gtk.RESPONSE_YES: ev.Session.HandleConfirmOrDeny(ev.From, true) case gtk.RESPONSE_NO: ev.Session.HandleConfirmOrDeny(ev.From, false) default: // We got a different response, such as a close of the window. In this case we want // to keep the subscription request open } confirmDialog.Destroy() }) case events.Subscribed: jid := ev.Session.GetConfig().Account log.Printf("[%s] Subscribed to %s\n", jid, ev.From) u.rosterUpdated() case events.Unsubscribe: jid := ev.Session.GetConfig().Account log.Printf("[%s] Unsubscribed from %s\n", jid, ev.From) u.rosterUpdated() } }
func (u *gtkUI) addContactWindow() { accounts := make([]*account, 0, len(u.accounts)) for i := range u.accounts { acc := u.accounts[i] if acc.connected() { accounts = append(accounts, acc) } } dialog := presenceSubscriptionDialog(accounts, func(accountID, peer string) error { //TODO errors account, ok := u.roster.getAccount(accountID) if !ok { return fmt.Errorf(i18n.Local("There is no account with the id %q"), accountID) } if !account.connected() { return errors.New(i18n.Local("Can't send a contact request from an offline account")) } return account.session.RequestPresenceSubscription(peer) }) dialog.SetTransientFor(u.window) dialog.ShowAll() }
func (u *gtkUI) showAddAccountWindow() { c, _ := config.NewAccount() u.accountDialog(nil, c, func() { u.addAndSaveAccountConfig(c) u.notify(i18n.Local("Account added"), fmt.Sprintf(i18n.Local("The account %s was added successfully."), c.Account)) }) }
func (conv *conversationPane) onStartOtrSignal() { //TODO: enable/disable depending on the conversation's encryption state session := conv.account.session c, _ := session.ConversationManager().EnsureConversationWith(conv.to, conv.currentResource()) err := c.StartEncryptedChat(session, conv.currentResource()) if err != nil { log.Printf(i18n.Local("Failed to start encrypted chat: %s\n"), err.Error()) } else { conv.displayNotification(i18n.Local("Attempting to start a private conversation...")) } }
func (u *gtkUI) getMasterPassword(params config.EncryptionParameters, lastAttemptFailed bool) ([]byte, []byte, bool) { dialogID := "MasterPassword" pwdResultChan := make(chan string) var cleanup func() doInUIThread(func() { builder := newBuilder(dialogID) dialogOb := builder.getObj(dialogID) dialog := dialogOb.(gtki.Dialog) cleanup = dialog.Destroy passObj := builder.getObj("password") password := passObj.(gtki.Entry) msgObj := builder.getObj("passMessage") messageObj := msgObj.(gtki.Label) messageObj.SetSelectable(true) if lastAttemptFailed { messageObj.SetLabel(i18n.Local("Incorrect password entered, please try again.")) } builder.ConnectSignals(map[string]interface{}{ "on_save_signal": func() { passText, _ := password.GetText() if len(passText) > 0 { messageObj.SetLabel(i18n.Local("Checking password...")) pwdResultChan <- passText close(pwdResultChan) } }, "on_cancel_signal": func() { close(pwdResultChan) u.quit() }, }) dialog.SetTransientFor(u.window) dialog.ShowAll() }) pwd, ok := <-pwdResultChan if !ok { doInUIThread(cleanup) return nil, nil, false } l, r := config.GenerateKeys(pwd, params) doInUIThread(cleanup) return l, r, true }
func (conv *conversationPane) onEndOtrSignal() { //TODO: enable/disable depending on the conversation's encryption state session := conv.account.session err := session.ManuallyEndEncryptedChat(conv.to, conv.currentResource()) if err != nil { log.Printf(i18n.Local("Failed to terminate the encrypted chat: %s\n"), err.Error()) } else { conv.displayNotification(i18n.Local("Private conversation has ended.")) conv.updateSecurityWarning() } }
func createStatusMessage(from string, show, showStatus string, gone bool) string { tail := "" if gone { tail = i18n.Local("Offline") + extraOfflineStatus(show, showStatus) } else { tail = onlineStatus(show, showStatus) } if tail != "" { return from + i18n.Local(" is now ") + tail } return "" }
func (u *gtkUI) showAddAccountWindow() error { c, err := config.NewAccount() if err != nil { return err } u.accountDialog(nil, c, func() { u.addAndSaveAccountConfig(c) u.notify(i18n.Local("Account added"), fmt.Sprintf(i18n.Local("The account %s was added successfully."), c.Account)) }) return nil }
// HandleErrorMessage is called when asked to handle a specific error message func (e *OtrEventHandler) HandleErrorMessage(error otr3.ErrorCode) []byte { log.Printf("HandleErrorMessage(%s)", error.String()) switch error { case otr3.ErrorCodeEncryptionError: return []byte(i18n.Local("Error occurred encrypting message.")) case otr3.ErrorCodeMessageUnreadable: return []byte(i18n.Local("You transmitted an unreadable encrypted message.")) case otr3.ErrorCodeMessageMalformed: return []byte(i18n.Local("You transmitted a malformed data message.")) case otr3.ErrorCodeMessageNotInPrivate: return []byte(i18n.Local("You sent encrypted data to a peer, who wasn't expecting it.")) } return nil }
func (account *account) createXMLConsoleItem(r *roster) gtki.MenuItem { consoleItem, _ := g.gtk.MenuItemNewWithMnemonic(i18n.Local("XML Console")) consoleItem.Connect("activate", func() { builder := newBuilder("XMLConsole") console := builder.getObj("XMLConsole").(gtki.Dialog) buf := builder.getObj("consoleContent").(gtki.TextBuffer) console.SetTransientFor(r.ui.window) console.SetTitle(strings.Replace(console.GetTitle(), "ACCOUNT_NAME", account.session.GetConfig().Account, -1)) log := account.session.GetInMemoryLog() buf.Delete(buf.GetStartIter(), buf.GetEndIter()) if log != nil { buf.Insert(buf.GetEndIter(), log.String()) } builder.ConnectSignals(map[string]interface{}{ "on_refresh_signal": func() { buf.Delete(buf.GetStartIter(), buf.GetEndIter()) if log != nil { buf.Insert(buf.GetEndIter(), log.String()) } }, "on_close_signal": func() { console.Destroy() }, }) console.ShowAll() }) return consoleItem }
func (account *account) createDumpInfoItem(r *roster) gtki.MenuItem { dumpInfoItem, _ := g.gtk.MenuItemNewWithMnemonic(i18n.Local("Dump info")) dumpInfoItem.Connect("activate", func() { r.debugPrintRosterFor(account.session.GetConfig().Account) }) return dumpInfoItem }
func (account *account) buildNotification(template, msg string) *gtk.InfoBar { builder := builderForDefinition(template) builder.ConnectSignals(map[string]interface{}{ "handleResponse": func(info *gtk.InfoBar, response gtk.ResponseType) { if response != gtk.RESPONSE_CLOSE { return } info.Hide() info.Destroy() }, }) obj, _ := builder.GetObject("infobar") infoBar := obj.(*gtk.InfoBar) obj, _ = builder.GetObject("message") msgLabel := obj.(*gtk.Label) msgLabel.SetSelectable(true) text := fmt.Sprintf(i18n.Local(msg), account.session.GetConfig().Account) msgLabel.SetText(text) return infoBar }
func (u *gtkUI) watchCommands() { for command := range u.commands { switch c := command.(type) { case executable: c.execute(u) case client.AuthorizeFingerprintCmd: account := c.Account uid := c.Peer fpr := c.Fingerprint //TODO: it could be a different pointer, //find the account by ID() account.AuthorizeFingerprint(uid, fpr) u.ExecuteCmd(client.SaveApplicationConfigCmd{}) ac := u.findAccountForSession(c.Session.(access.Session)) if ac != nil { peer := c.Peer convWindowNowOrLater(ac, peer, u, func(cv conversationView) { cv.displayNotification(fmt.Sprintf(i18n.Local("You have verified the identity of %s."), peer)) }) } case client.SaveInstanceTagCmd: account := c.Account account.InstanceTag = c.InstanceTag u.ExecuteCmd(client.SaveApplicationConfigCmd{}) case client.SaveApplicationConfigCmd: u.SaveConfig() } } }
func buildVerifyIdentityNotification(acc *account, peer, resource string, win gtki.Window) gtki.InfoBar { builder := newBuilder("VerifyIdentityNotification") obj := builder.getObj("infobar") infoBar := obj.(gtki.InfoBar) obj = builder.getObj("message") message := obj.(gtki.Label) message.SetSelectable(true) text := fmt.Sprintf(i18n.Local("You have not verified the identity of %s"), peer) message.SetText(text) obj = builder.getObj("button_verify") button := obj.(gtki.Button) button.Connect("clicked", func() { doInUIThread(func() { resp := verifyFingerprintDialog(acc, peer, resource, win) if resp == gtki.RESPONSE_YES { infoBar.Hide() infoBar.Destroy() } }) }) infoBar.ShowAll() return infoBar }
// NewConversation will create a new OTR conversation with the given peer //TODO: why creating a conversation is coupled to the account config and the session //TODO: does the creation of the OTR event handler need to be guarded with a lock? func (s *session) NewConversation(peer string) *otr3.Conversation { conversation := &otr3.Conversation{} conversation.SetOurKeys(s.privateKeys) conversation.SetFriendlyQueryMessage(i18n.Local("Your peer has requested a private conversation with you, but your client doesn't seem to support the OTR protocol.")) instanceTag := conversation.InitializeInstanceTag(s.GetConfig().InstanceTag) if s.GetConfig().InstanceTag != instanceTag { s.cmdManager.ExecuteCmd(client.SaveInstanceTagCmd{ Account: s.GetConfig(), InstanceTag: instanceTag, }) } s.GetConfig().SetOTRPoliciesFor(peer, conversation) eh, ok := s.otrEventHandler[peer] if !ok { eh = new(event.OtrEventHandler) eh.Account = s.GetConfig().Account eh.Peer = peer notificationsChan := make(chan string) eh.Notifications = notificationsChan go s.listenToNotifications(notificationsChan, peer) conversation.SetSMPEventHandler(eh) conversation.SetErrorMessageHandler(eh) conversation.SetMessageEventHandler(eh) conversation.SetSecurityEventHandler(eh) s.otrEventHandler[peer] = eh } return conversation }
// getProxyTypeFor will return the proxy type for the given i18n proxy name func getProxyTypeFor(act string) string { for _, px := range proxyTypes { if act == i18n.Local(px[1]) { return px[0] } } return "" }
// Send will send the given message to the receiver given func (s *session) Send(to, resource string, msg string) error { conn, ok := s.connection() if ok { log.Printf("<- to=%v {%v}\n", utils.ComposeFullJid(to, resource), msg) return conn.Send(utils.ComposeFullJid(to, resource), msg) } return &access.OfflineError{Msg: i18n.Local("Couldn't send message since we are not connected")} }
// EncryptAndSendTo encrypts and sends the message to the given peer func (s *session) EncryptAndSendTo(peer, resource string, message string) error { //TODO: review whether it should create a conversation if s.IsConnected() { conversation, _ := s.convManager.EnsureConversationWith(peer, resource) return conversation.Send(s, resource, []byte(message)) } return &access.OfflineError{Msg: i18n.Local("Couldn't send message since we are not connected")} }
func (u *gtkUI) captureInitialMasterPassword(k func(), onCancel func()) { dialogID := "CaptureInitialMasterPassword" builder := newBuilder(dialogID) dialogOb := builder.getObj(dialogID) pwdDialog := dialogOb.(gtki.Dialog) passObj := builder.getObj("password") password := passObj.(gtki.Entry) pass2Obj := builder.getObj("password2") password2 := pass2Obj.(gtki.Entry) msgObj := builder.getObj("passMessage") messageObj := msgObj.(gtki.Label) messageObj.SetSelectable(true) builder.ConnectSignals(map[string]interface{}{ "on_save_signal": func() { passText1, _ := password.GetText() passText2, _ := password2.GetText() if len(passText1) == 0 { messageObj.SetLabel(i18n.Local("Password can not be empty - please try again")) password.GrabFocus() } else if passText1 != passText2 { messageObj.SetLabel(i18n.Local("Passwords have to be the same - please try again")) password.GrabFocus() } else { u.keySupplier = &onetimeSavedPassword{ savedPassword: passText1, realF: u.keySupplier, } pwdDialog.Destroy() k() } }, "on_cancel_signal": func() { pwdDialog.Destroy() onCancel() }, }) doInUIThread(func() { pwdDialog.SetTransientFor(u.window) pwdDialog.ShowAll() }) }
func buildVerifyFingerprintDialog(accountName string, ourFp []byte, uid string, theirFp []byte) *gtk.Dialog { var message string var builderName string if theirFp == nil { builderName = "VerifyFingerprintUnknown" message = fmt.Sprintf(i18n.Local( "You can't verify the fingerprint for %s yet.\n"+ "You first have to start an encrypted conversation with them.", ), uid) } else { m := i18n.Local(` Is this the correct fingerprint for %[1]s? Fingerprint for you (%[3]s): %[4]s Purported fingerprint for %[1]s: %[2]s `) message = fmt.Sprintf(m, uid, config.FormatFingerprint(theirFp), accountName, config.FormatFingerprint(ourFp), ) builderName = "VerifyFingerprint" } builder := builderForDefinition(builderName) obj, _ := builder.GetObject("dialog") dialog := obj.(*gtk.Dialog) obj, _ = builder.GetObject("message") l := obj.(*gtk.Label) l.SetText(message) l.SetSelectable(true) dialog.SetTitle(fmt.Sprintf(i18n.Local("Verify fingerprint for %s"), uid)) return dialog }
func (account *account) createConnectionItem() gtki.MenuItem { connInfoItem, _ := g.gtk.MenuItemNewWithMnemonic(i18n.Local("Connection _information...")) connInfoItem.Connect("activate", account.connectionInfo) connInfoItem.SetSensitive(!account.session.IsDisconnected()) account.observeConnectionEvents(func() { connInfoItem.SetSensitive(!account.session.IsDisconnected()) }) return connInfoItem }
func (u *gtkUI) handleNotificationEvent(ev events.Notification) { peer := ev.Peer account := u.findAccountForSession(ev.Session) convWin, err := u.roster.openConversationView(account, peer, false) if err != nil { return } convWin.displayNotification(i18n.Local(ev.Notification)) }
func (u *gtkUI) showFingerprintsForPeer(jid string, account *account) { builder := builderForDefinition("PeerFingerprints") dialog := getObjIgnoringErrors(builder, "dialog").(*gtk.Dialog) info := getObjIgnoringErrors(builder, "information").(*gtk.Label) grid := getObjIgnoringErrors(builder, "grid").(*gtk.Grid) info.SetSelectable(true) fprs := []*config.Fingerprint{} p, ok := account.session.GetConfig().GetPeer(jid) if ok { fprs = p.Fingerprints } if len(fprs) == 0 { info.SetText(fmt.Sprintf(i18n.Local("There are no known fingerprints for %s"), jid)) } else { info.SetText(fmt.Sprintf(i18n.Local("These are the fingerprints known for %s:"), jid)) } for ix, fpr := range fprs { flabel, _ := gtk.LabelNew(config.FormatFingerprint(fpr.Fingerprint)) flabel.SetSelectable(true) trusted := i18n.Local("not trusted") if fpr.Trusted { trusted = i18n.Local("trusted") } ftrusted, _ := gtk.LabelNew(trusted) ftrusted.SetSelectable(true) grid.Attach(flabel, 0, ix, 1, 1) grid.Attach(ftrusted, 1, ix, 1, 1) } builder.ConnectSignals(map[string]interface{}{ "on_close_signal": func() { dialog.Destroy() }, }) dialog.SetTransientFor(u.window) dialog.ShowAll() }
func authorizePresenceSubscriptionDialog(parent gtki.Window, from string) gtki.MessageDialog { builder := newBuilder("AuthorizeSubscription") confirmDialog := builder.getObj("dialog").(gtki.MessageDialog) text := fmt.Sprintf(i18n.Local("%s wants to talk to you. Is that ok?"), from) confirmDialog.SetProperty("text", text) confirmDialog.SetTransientFor(parent) return confirmDialog }
func (conv *conversationPane) appendDelayed(from string, timestamp time.Time, message []byte) { conv.appendToHistory(timestamp, false, taggableText{"outgoingUser", i18n.Local("(Not sent yet) ")}, taggableText{"outgoingDelayedUser", from}, taggableText{ text: ": ", }, taggableText{"outgoingDelayedText", string(message)}, ) }
// EncryptAndSendTo encrypts and sends the message to the given peer func (s *session) EncryptAndSendTo(peer, resource string, message string) (trace int, delayed bool, err error) { //TODO: review whether it should create a conversation if s.IsConnected() { conversation, _ := s.convManager.EnsureConversationWith(peer, resource) trace, err = conversation.Send(s, resource, []byte(message)) eh := s.otrEventHandler[peer] delayed = eh.ConsumeDelayedState(trace) return } return 0, false, &access.OfflineError{Msg: i18n.Local("Couldn't send message since we are not connected")} }
func buildBadUsernameNotification(msg string) *gtk.InfoBar { b := builderForDefinition("BadUsernameNotification") infoBar := getObjIgnoringErrors(b, "infobar").(*gtk.InfoBar) message := getObjIgnoringErrors(b, "message").(*gtk.Label) message.SetSelectable(true) message.SetText(fmt.Sprintf(i18n.Local(msg))) return infoBar }
func (account *account) createDisconnectItem() gtki.MenuItem { disconnectItem, _ := g.gtk.MenuItemNewWithMnemonic(i18n.Local("_Disconnect")) disconnectItem.Connect("activate", func() { account.session.SetWantToBeOnline(false) account.disconnect() }) disconnectItem.SetSensitive(!account.session.IsDisconnected()) account.observeConnectionEvents(func() { disconnectItem.SetSensitive(!account.session.IsDisconnected()) }) return disconnectItem }
func buildBadUsernameNotification(msg string) gtki.InfoBar { assertInUIThread() b := newBuilder("BadUsernameNotification") infoBar := b.getObj("infobar").(gtki.InfoBar) message := b.getObj("message").(gtki.Label) message.SetSelectable(true) message.SetText(fmt.Sprintf(i18n.Local(msg))) return infoBar }