core-side changes to enable avatars in chat

dbus-notify
Zlatin Balevsky 2022-06-02 13:39:00 +01:00
parent 9fbf6d1b5d
commit a0ac1cd3f4
No known key found for this signature in database
GPG Key ID: A72832072D525E41
10 changed files with 122 additions and 15 deletions

View File

@ -414,7 +414,7 @@ public class Core {
eventBus.register(UIFetchCertificatesEvent.class, certificateClient)
log.info("initializing chat server")
chatServer = new ChatServer(eventBus, props, trustService, me, spk)
chatServer = new ChatServer(eventBus, props, trustService, me, profileSupplier, spk)
eventBus.with {
register(ChatMessageEvent.class, chatServer)
register(ChatDisconnectionEvent.class, chatServer)
@ -445,7 +445,7 @@ public class Core {
eventBus.register(ResultsEvent.class, searchManager)
log.info("initializing chat manager")
chatManager = new ChatManager(eventBus, me, i2pConnector, trustService, props)
chatManager = new ChatManager(eventBus, me, profileSupplier, i2pConnector, trustService, props)
eventBus.with {
register(UIConnectChatEvent.class, chatManager)
register(UIDisconnectChatEvent.class, chatManager)

View File

@ -1,5 +1,8 @@
package com.muwire.core.chat
import com.muwire.core.profile.MWProfile
import com.muwire.core.profile.MWProfileHeader
import java.nio.charset.StandardCharsets
import java.util.concurrent.Executor
import java.util.concurrent.Executors
@ -15,6 +18,8 @@ import com.muwire.core.util.DataUtil
import groovy.util.logging.Log
import java.util.function.Supplier
@Log
class ChatClient implements Closeable {
@ -25,6 +30,7 @@ class ChatClient implements Closeable {
private final I2PConnector connector
private final EventBus eventBus
private final Persona host, me
private final Supplier<MWProfile> profileSupplier
private final TrustService trustService
private final MuWireSettings settings
@ -33,12 +39,15 @@ class ChatClient implements Closeable {
private long lastRejectionTime
private Thread connectThread
ChatClient(I2PConnector connector, EventBus eventBus, Persona host, Persona me, TrustService trustService,
ChatClient(I2PConnector connector, EventBus eventBus,
Persona host, Persona me, Supplier<MWProfile> profileSupplier,
TrustService trustService,
MuWireSettings settings) {
this.connector = connector
this.eventBus = eventBus
this.host = host
this.me = me
this.profileSupplier = profileSupplier
this.trustService = trustService
this.settings = settings
}
@ -67,6 +76,9 @@ class ChatClient implements Closeable {
write("IRC\r\n".getBytes(StandardCharsets.US_ASCII))
write("Version:${Constants.CHAT_VERSION}\r\n".getBytes(StandardCharsets.US_ASCII))
write("Persona:${me.toBase64()}\r\n".getBytes(StandardCharsets.US_ASCII))
MWProfile profile = profileSupplier.get()
if (profile != null)
write("ProfileHeader:${profile.getHeader().toBase64()}\r\n".getBytes(StandardCharsets.US_ASCII))
write("\r\n".getBytes(StandardCharsets.US_ASCII))
flush()
}
@ -102,8 +114,8 @@ class ChatClient implements Closeable {
synchronized(this) {
if (!connectInProgress)
return
connection = new ChatConnection(eventBus, endpoint, host, false,
trustService, settings, Constants.CHAT_VERSION)
connection = new ChatConnection(eventBus, endpoint, host, profileSupplier.get()?.getHeader(),
false, trustService, settings, Constants.CHAT_VERSION)
connection.start()
}
eventBus.publish(new ChatConnectionEvent(status : ChatConnectionAttemptStatus.SUCCESSFUL, persona : host,

View File

@ -1,5 +1,7 @@
package com.muwire.core.chat
import com.muwire.core.profile.MWProfileHeader
import java.nio.charset.StandardCharsets
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue
@ -31,6 +33,7 @@ class ChatConnection implements ChatLink {
private final EventBus eventBus
private final Endpoint endpoint
private final Persona persona
private final MWProfileHeader profileHeader
private final boolean incoming
private final TrustService trustService
private final MuWireSettings settings
@ -49,11 +52,12 @@ class ChatConnection implements ChatLink {
private volatile long lastPingSentTime
ChatConnection(EventBus eventBus, Endpoint endpoint, Persona persona, boolean incoming,
TrustService trustService, MuWireSettings settings, int version) {
ChatConnection(EventBus eventBus, Endpoint endpoint, Persona persona, MWProfileHeader profileHeader,
boolean incoming, TrustService trustService, MuWireSettings settings, int version) {
this.eventBus = eventBus
this.endpoint = endpoint
this.persona = persona
this.profileHeader = profileHeader
this.incoming = incoming
this.trustService = trustService
this.settings = settings
@ -90,6 +94,11 @@ class ChatConnection implements ChatLink {
persona
}
@Override
public MWProfileHeader getProfileHeader() {
profileHeader
}
@Override
public void close() {
if (!running.compareAndSet(true, false)) {
@ -211,7 +220,7 @@ class ChatConnection implements ChatLink {
return
}
def event = new ChatMessageEvent( uuid : uuid, payload : payload, sender : sender,
host : host, room : room, chatTime : chatTime, sig : sig)
host : host, link: this, room : room, chatTime : chatTime, sig : sig)
eventBus.publish(event)
if (!incoming)
incomingEvents.put(event)

View File

@ -3,9 +3,11 @@ package com.muwire.core.chat;
import java.io.Closeable;
import com.muwire.core.Persona;
import com.muwire.core.profile.MWProfileHeader;
public interface ChatLink extends Closeable {
public Persona getPersona();
public MWProfileHeader getProfileHeader();
public boolean isUp();
public void sendChat(ChatMessageEvent e);
public void sendLeave(Persona p);

View File

@ -1,5 +1,7 @@
package com.muwire.core.chat
import com.muwire.core.profile.MWProfile
import java.util.concurrent.ConcurrentHashMap
import com.muwire.core.EventBus
@ -8,19 +10,23 @@ import com.muwire.core.Persona
import com.muwire.core.connection.I2PConnector
import com.muwire.core.trust.TrustService
import java.util.function.Supplier
class ChatManager {
private final EventBus eventBus
private final Persona me
private final I2PConnector connector
private final TrustService trustService
private final MuWireSettings settings
private final Supplier<MWProfile> profileSupplier
private final Map<Persona, ChatClient> clients = new ConcurrentHashMap<>()
ChatManager(EventBus eventBus, Persona me, I2PConnector connector, TrustService trustService,
MuWireSettings settings) {
ChatManager(EventBus eventBus, Persona me, Supplier<MWProfile> profileSupplier,
I2PConnector connector, TrustService trustService, MuWireSettings settings) {
this.eventBus = eventBus
this.me = me
this.profileSupplier = profileSupplier
this.connector = connector
this.trustService = trustService
this.settings = settings

View File

@ -7,6 +7,7 @@ class ChatMessageEvent extends Event {
UUID uuid
String payload
Persona sender, host
ChatLink link
String room
long chatTime
byte [] sig

View File

@ -1,8 +1,13 @@
package com.muwire.core.chat
import com.muwire.core.profile.MWProfile
import com.muwire.core.profile.MWProfileHeader
import java.nio.charset.StandardCharsets
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import java.util.function.Predicate
import java.util.function.Supplier
import java.util.logging.Level
import java.util.stream.Collectors
@ -31,6 +36,7 @@ class ChatServer {
private final MuWireSettings settings
private final TrustService trustService
private final Persona me
private final Supplier<MWProfile> profileSupplier
private final SigningPrivateKey spk
private final Map<Destination, ChatLink> connections = new ConcurrentHashMap()
@ -40,11 +46,13 @@ class ChatServer {
private final AtomicBoolean running = new AtomicBoolean()
ChatServer(EventBus eventBus, MuWireSettings settings, TrustService trustService, Persona me, SigningPrivateKey spk) {
ChatServer(EventBus eventBus, MuWireSettings settings, TrustService trustService,
Persona me, Supplier<MWProfile> profileSupplier, SigningPrivateKey spk) {
this.eventBus = eventBus
this.settings = settings
this.trustService = trustService
this.me = me
this.profileSupplier = profileSupplier
this.spk = spk
Timer timer = new Timer("chat-server-pinger", true)
@ -96,6 +104,14 @@ class ChatServer {
Persona client = new Persona(new ByteArrayInputStream(Base64.decode(headers['Persona'])))
if (client.destination != endpoint.destination)
throw new Exception("Client destination mismatch")
MWProfileHeader profileHeader = null
if (headers.containsKey("ProfileHeader")) {
byte [] decoded = Base64.decode(headers['ProfileHeader'])
profileHeader = new MWProfileHeader(new ByteArrayInputStream(decoded))
if (profileHeader.getPersona() != client)
throw new Exception("profile and persona mismatch")
}
if (!running.get()) {
os.write("400 Chat Not Enabled\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
@ -128,7 +144,7 @@ class ChatServer {
flush()
}
ChatConnection connection = new ChatConnection(eventBus, endpoint, client, true,
ChatConnection connection = new ChatConnection(eventBus, endpoint, client, profileHeader, true,
trustService, settings, version)
connections.put(endpoint.destination, connection)
joinRoom(client, CONSOLE)
@ -244,11 +260,24 @@ class ChatServer {
private void processJoin(String room, ChatMessageEvent e) {
joinRoom(e.sender, room)
// tell everyone in the room about this person has joined
MWProfileHeader header = getHeaderFromConnection(e.link)
ChatMessageEvent profileBroadcast = null
if (header != null)
profileBroadcast = buildProfileBroadcast(header, room)
rooms[room].each {
if (it == e.sender)
return
connections[it.destination].sendChat(e)
if (profileBroadcast != null)
connections[it.destination].sendChat(profileBroadcast)
}
// tell the new joiner who else is in the room
String payload = rooms[room].stream().filter({it != e.sender}).map({it.toBase64()})
.collect(Collectors.joining(","))
if (payload.length() == 0) {
@ -262,12 +291,46 @@ class ChatServer {
uuid : uuid,
payload : payload,
sender : me,
host : me,
host : me,
link: LocalChatLink.INSTANCE,
room : room,
chatTime : now,
sig : sig
)
connections[e.sender.destination].sendChat(echo)
// send all profiles of people already joined
rooms[room].stream().
map({connections.get(it.destination)}).
map({getHeaderFromConnection(it)}).
filter({it != null}).
forEach {
profileBroadcast = buildProfileBroadcast(it, room)
connections[e.sender.destination].sendChat(profileBroadcast)
}
}
private MWProfileHeader getHeaderFromConnection(ChatLink link) {
if (link instanceof LocalChatLink)
return profileSupplier.get()?.getHeader()
return link.getProfileHeader()
}
private ChatMessageEvent buildProfileBroadcast(MWProfileHeader header, String room) {
UUID uuid = UUID.randomUUID()
long now = System.currentTimeMillis()
String payload = "/PROFILE ${header.toBase64()}"
byte [] sig = ChatConnection.sign(uuid, now, room, payload, me, me, spk)
new ChatMessageEvent(
uuid: uuid,
payload: payload,
sender: me,
host: me,
link: LocalChatLink.INSTANCE,
room: room,
chatTime: now,
sig: sig
)
}
private void processLeave(ChatMessageEvent e) {
@ -330,7 +393,8 @@ class ChatServer {
uuid : uuid,
payload : payload,
sender : me,
host : me,
host : me,
link : LocalChatLink.INSTANCE,
room : room,
chatTime : now,
sig : sig

View File

@ -1,5 +1,7 @@
package com.muwire.core.chat
import com.muwire.core.profile.MWProfileHeader
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue
@ -46,4 +48,8 @@ class LocalChatLink implements ChatLink {
public Persona getPersona() {
null
}
public MWProfileHeader getProfileHeader() {
null
}
}

View File

@ -1,5 +1,7 @@
package com.muwire.gui
import com.muwire.core.chat.LocalChatLink
import static com.muwire.gui.Translator.trans
import griffon.core.artifact.GriffonController
@ -95,7 +97,8 @@ class ChatRoomController {
def event = new ChatMessageEvent(uuid : uuid,
payload : command.source,
sender : model.core.me,
host : model.host,
host : model.host,
link: LocalChatLink.INSTANCE,
room : room,
chatTime : now,
sig : sig)
@ -172,6 +175,7 @@ class ChatRoomController {
payload : "/LEAVE",
sender : model.core.me,
host : model.host,
link : LocalChatLink.INSTANCE,
room : model.room,
chatTime : now,
sig : sig)
@ -266,6 +270,7 @@ class ChatRoomController {
payload : join,
sender : model.core.me,
host : model.host,
link: LocalChatLink.INSTANCE,
room : ChatServer.CONSOLE,
chatTime : now,
sig : sig

View File

@ -3,6 +3,7 @@ package com.muwire.gui
import com.muwire.core.chat.ChatConnection
import com.muwire.core.chat.ChatMessageEvent
import com.muwire.core.chat.ChatServer
import com.muwire.core.chat.LocalChatLink
import griffon.core.GriffonApplication
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
@ -39,6 +40,7 @@ class ChatServerController {
payload: command,
sender: model.core.me,
host: model.host,
link: LocalChatLink.INSTANCE,
room: ChatServer.CONSOLE,
chatTime: now,
sig: sig)