Example #1
0
func (s *Session) processPresence(stanza *xmpp.ClientPresence) {
	gone := false

	switch stanza.Type {
	case "subscribe":
		// This is a subscription request
		jid := xmpp.RemoveResourceFromJid(stanza.From)
		info(s.term, jid+" wishes to see when you're online. Use '/confirm "+jid+"' to confirm (or likewise with /deny to decline)")
		s.pendingSubscribes[jid] = stanza.Id
		s.input.AddUser(jid)
		return
	case "unavailable":
		gone = true
	case "":
		break
	default:
		return
	}

	from := xmpp.RemoveResourceFromJid(stanza.From)

	if gone {
		if _, ok := s.knownStates[from]; !ok {
			// They've gone, but we never knew they were online.
			return
		}
		delete(s.knownStates, from)
	} else {
		if _, ok := s.knownStates[from]; !ok && isAwayStatus(stanza.Show) {
			// Skip people who are initially away.
			return
		}

		if lastState, ok := s.knownStates[from]; ok && lastState == stanza.Show {
			// No change. Ignore.
			return
		}
		s.knownStates[from] = stanza.Show
	}

	if !s.config.HideStatusUpdates {
		var line []byte
		line = append(line, s.term.Escape.Magenta...)
		line = append(line, []byte(from)...)
		line = append(line, ':')
		line = append(line, s.term.Escape.Reset...)
		line = append(line, ' ')
		if gone {
			line = append(line, []byte("offline")...)
		} else if len(stanza.Show) > 0 {
			line = append(line, []byte(stanza.Show)...)
		} else {
			line = append(line, []byte("online")...)
		}
		line = append(line, ' ')
		line = append(line, []byte(stanza.Status)...)
		line = append(line, '\n')
		s.term.Write(line)
	}
}
Example #2
0
func (s *Session) processClientMessage(stanza *xmpp.ClientMessage) {
	from := xmpp.RemoveResourceFromJid(stanza.From)
	conversation, ok := s.conversations[from]
	if !ok {
		conversation = new(otr.Conversation)
		conversation.PrivateKey = s.privateKey
		s.conversations[from] = conversation
	}

	out, encrypted, change, toSend, err := conversation.Process([]byte(stanza.Body))
	if err != nil {
		alert(s.term, "While processing message from "+from+": "+err.Error())
	}
	for _, msg := range toSend {
		s.conn.Send(stanza.From, string(msg))
	}
	switch change {
	case otr.NewKeys:
		fpr := conversation.TheirPublicKey.Fingerprint()
		info(s.term, fmt.Sprintf("New OTR session with %s established", from))
		info(s.term, fmt.Sprintf("  their fingerprint: %x", fpr))
		info(s.term, fmt.Sprintf("  session id: %x", conversation.SSID))
		knownUserId := s.config.UserIdForFingerprint(fpr)
		if len(knownUserId) == 0 {
			alert(s.term, "Fingerprint is unknown. You should use /auth or /authqa to verify their identity")
		} else {
			if knownUserId == from {
				info(s.term, "Fingerprint is verified")
			} else {
				alert(s.term, fmt.Sprintf("Fingerprint is known, but for %s", knownUserId))
			}
		}
	case otr.ConversationEnded:
		info(s.term, fmt.Sprintf("%s has ended the secure conversation. You should do likewise with /endotr", from))
	case otr.SMPSecretNeeded:
		info(s.term, fmt.Sprintf("%s is attempting to authenticate. Please supply mutual shared secret with /smp", from))
		if question := conversation.SMPQuestion(); len(question) > 0 {
			info(s.term, fmt.Sprintf("%s asks: %s", from, question))
		}
	case otr.SMPComplete:
		info(s.term, fmt.Sprintf("Authentication with %s successful", from))
		fpr := conversation.TheirPublicKey.Fingerprint()
		if len(s.config.UserIdForFingerprint(fpr)) == 0 {
			s.config.KnownFingerprints = append(s.config.KnownFingerprints, KnownFingerprint{fingerprint: fpr, UserId: from})
		}
		s.config.Save()
	}
	if len(out) == 0 {
		return
	}

	var line []byte
	if encrypted {
		line = append(line, s.term.Escape.Green...)
	} else {
		line = append(line, s.term.Escape.Red...)
	}
	if s.lastMessageFrom != from {
		line = append(line, []byte(from)...)
		s.lastMessageFrom = from
	}
	line = append(line, ':')
	line = append(line, s.term.Escape.Reset...)
	line = append(line, ' ')
	line = append(line, out...)
	line = append(line, '\n')
	s.term.Write(line)
	s.maybeNotify()
}
Example #3
0
func (s *Session) processClientMessage(stanza *xmpp.ClientMessage) {
	from := xmpp.RemoveResourceFromJid(stanza.From)

	if stanza.Type == "error" {
		alert(s.term, "Error reported from "+from+": "+stanza.Body)
		return
	}

	conversation, ok := s.conversations[from]
	if !ok {
		conversation = new(otr.Conversation)
		conversation.PrivateKey = s.privateKey
		s.conversations[from] = conversation
	}

	out, encrypted, change, toSend, err := conversation.Receive([]byte(stanza.Body))
	if err != nil {
		alert(s.term, "While processing message from "+from+": "+err.Error())
		s.conn.Send(stanza.From, otr.ErrorPrefix+"Error processing message")
	}
	for _, msg := range toSend {
		s.conn.Send(stanza.From, string(msg))
	}
	switch change {
	case otr.NewKeys:
		info(s.term, fmt.Sprintf("New OTR session with %s established at %s", from, time.Now().Format(time.RubyDate)))
		printConversationInfo(*s, from, conversation)
	case otr.ConversationEnded:
		// This is probably unsafe without a policy that _forces_ crypto to
		// _everyone_ by default and refuses plaintext. Users might not notice
		// their buddy has ended a session, which they have also ended, and they
		// might send a plain text message. So we should ensure they _want_ this
		// feature and have set it as an explicit preference.
		if s.config.OTRAutoTearDown {
			if s.conversations[from] == nil {
				alert(s.term, fmt.Sprintf("No secure session established; unable to automatically tear down OTR conversation with %s.", from))
				break
			} else {
				info(s.term, fmt.Sprintf("%s has ended the secure conversation.", from))
				msgs := conversation.End()
				for _, msg := range msgs {
					s.conn.Send(from, string(msg))
				}
				info(s.term, fmt.Sprintf("Secure session with %s has been automatically ended. Messages will be sent in the clear until another OTR session is established.", from))
			}
		} else {
			info(s.term, fmt.Sprintf("%s has ended the secure conversation. You should do likewise with /otr-end %s", from, from))
		}
	case otr.SMPSecretNeeded:
		info(s.term, fmt.Sprintf("%s is attempting to authenticate. Please supply mutual shared secret with /otr-auth user secret", from))
		if question := conversation.SMPQuestion(); len(question) > 0 {
			info(s.term, fmt.Sprintf("%s asks: %s", from, question))
		}
	case otr.SMPComplete:
		info(s.term, fmt.Sprintf("Authentication with %s successful", from))
		fpr := conversation.TheirPublicKey.Fingerprint()
		if len(s.config.UserIdForFingerprint(fpr)) == 0 {
			s.config.KnownFingerprints = append(s.config.KnownFingerprints, KnownFingerprint{fingerprint: fpr, UserId: from})
		}
		s.config.Save()
	case otr.SMPFailed:
		alert(s.term, fmt.Sprintf("Authentication with %s failed", from))
	}

	if len(out) == 0 {
		return
	}

	detectedOTRVersion := 0
	// We don't need to alert about tags encoded inside of messages that are
	// already encrypted with OTR
	whitespaceTagLength := len(OTRWhitespaceTagStart) + len(OTRWhiteSpaceTagV1)
	if !encrypted && len(out) >= whitespaceTagLength {
		whitespaceTag := out[len(out)-whitespaceTagLength:]
		if bytes.Equal(whitespaceTag[:len(OTRWhitespaceTagStart)], OTRWhitespaceTagStart) {
			if bytes.HasSuffix(whitespaceTag, OTRWhiteSpaceTagV1) {
				info(s.term, fmt.Sprintf("%s appears to support OTRv1. You should encourage them to upgrade their OTR client!", from))
				detectedOTRVersion = 1
			}
			if bytes.HasSuffix(whitespaceTag, OTRWhiteSpaceTagV2) {
				detectedOTRVersion = 2
			}
			if bytes.HasSuffix(whitespaceTag, OTRWhiteSpaceTagV3) {
				detectedOTRVersion = 3
			}
		}
	}

	if s.config.OTRAutoStartSession && detectedOTRVersion >= 2 {
		info(s.term, fmt.Sprintf("%s appears to support OTRv%d. We are attempting to start an OTR session with them.", from, detectedOTRVersion))
		s.conn.Send(from, otr.QueryMessage)
	} else if s.config.OTRAutoStartSession && detectedOTRVersion == 1 {
		info(s.term, fmt.Sprintf("%s appears to support OTRv%d. You should encourage them to upgrade their OTR client!", from, detectedOTRVersion))
	}

	var line []byte
	if encrypted {
		line = append(line, s.term.Escape.Green...)
	} else {
		line = append(line, s.term.Escape.Red...)
	}

	var timestamp string
	var messageTime time.Time
	if stanza.Delay != nil && len(stanza.Delay.Stamp) > 0 {
		// An XEP-0203 Delayed Delivery <delay/> element exists for
		// this message, meaning that someone sent it while we were
		// offline. Let's show the timestamp for when the message was
		// sent, rather than time.Now().
		messageTime, err = time.Parse(time.RFC3339, stanza.Delay.Stamp)
		if err != nil {
			alert(s.term, "Can not parse Delayed Delivery timestamp, using quoted string instead.")
			timestamp = fmt.Sprintf("%q", stanza.Delay.Stamp)
		}
	} else {
		messageTime = time.Now()
	}
	if len(timestamp) == 0 {
		timestamp = messageTime.Format(time.RubyDate)
	}

	t := fmt.Sprintf("(%s) %s: ", timestamp, from)
	line = append(line, []byte(t)...)
	line = append(line, s.term.Escape.Reset...)
	line = appendTerminalEscaped(line, stripHTML(out))
	line = append(line, '\n')
	if s.config.Bell {
		line = append(line, '\a')
	}
	s.term.Write(line)
	s.maybeNotify()
}
Example #4
0
func Echo(ws *websocket.Conn) {
	fmt.Println("socket open")
	var recMes message

	err := websocket.JSON.Receive(ws, &recMes)
	if err != nil {
		fmt.Println("Can't receive message")
	}

	if recMes.Type != "login" {
		fmt.Println("not login message")
		return
	}

	userName := strings.Split(recMes.Data["UserName"], "@")
	if len(userName) < 2 {
		userName = []string{userName[0], ""}
	}

	xmppConfig := xmpp.Config{nil, nil, nil, nil, false, false, false, []byte("")}
	talk, err := xmpp.Dial(recMes.Data["Server"], userName[0], userName[1], recMes.Data["Password"], &xmppConfig)
	if err != nil {
		fmt.Println("login error")
		return
	}

	senMes := message{"login", nil}

	if err := websocket.JSON.Send(ws, senMes); err != nil {
		fmt.Println("Can't send")
		return
	}

	fmt.Println("Ready")

	rosterReply, _, err := talk.RequestRoster()
	if err != nil {
		fmt.Println("Can't roster")
	}

	talk.SignalPresence("")

	ses := session{
		talk: talk,
		ws:   ws,
	}

	stanzaChan := make(chan xmpp.Stanza)
	go ses.receiveMessage(stanzaChan)

	webSocketChan := make(chan string)
	go ses.receiveWebSocket(webSocketChan)

	for {
		select {
		case rosterStanza, ok := <-rosterReply:
			var roster []xmpp.RosterEntry
			if !ok {
				fmt.Println("fail to read roaster")
				return
			}
			if roster, err = xmpp.ParseRoster(rosterStanza); err != nil {
				fmt.Println("fail to parse roaster")
				return
			}
			if err := websocket.JSON.Send(ws, rosterMessage{"roster", roster}); err != nil {
				fmt.Println("Can't send")
				return
			}
			fmt.Println("Roster information sent to the client")
		case rawStanza, ok := <-stanzaChan:
			if !ok {
				fmt.Println("stanzaChan receive failed")
				return
			}
			switch stanza := rawStanza.Value.(type) {
			case *xmpp.ClientMessage:
				fmt.Println("Message comming")
				if stanza.Body == "" {
					break
				}
				chatMes := message{"chat", map[string]string{"Remote": xmpp.RemoveResourceFromJid(stanza.From), "Text": stanza.Body}}

				if err := websocket.JSON.Send(ws, chatMes); err != nil {
					fmt.Println("Can't send")
					break
				}
			case *xmpp.ClientPresence:
				fmt.Println("ClientPresence comming")
				fmt.Println(stanza.From, stanza.Type)

				var status string
				if len(stanza.Show) > 0 {
					status = stanza.Show
				} else {
					status = "available"
				}

				preMes := message{"presence", map[string]string{"Remote": xmpp.RemoveResourceFromJid(stanza.From), "Mode": status}}
				if err := websocket.JSON.Send(ws, preMes); err != nil {
					fmt.Println("Can't send")
					return
				}
			}
		case receivedMessage, ok := <-webSocketChan:
			if !ok {
				fmt.Println("webSocketChan receive failed")
				return
			}
			var sendMes message
			if err = json.Unmarshal([]byte(receivedMessage), &sendMes); err != nil {
				fmt.Println("Can't receive")
			} else {

				if err = talk.Send(sendMes.Data["Remote"], sendMes.Data["Text"]); err != nil {
					fmt.Println("failed to send")
				}
				fmt.Println("Message sent" + sendMes.Data["Remote"] + sendMes.Data["Text"])
			}
		}
	}
	fmt.Println("function ends")
}