aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/juick/server
diff options
context:
space:
mode:
authorGravatar alx2019-03-16 23:56:27 +0300
committerGravatar alx2019-03-16 23:56:27 +0300
commit06105f76dbfa3b65e63ed06f9c4d5107bd49ed88 (patch)
tree5702c01cec9688039d891f4a711878706101c1c5 /src/main/java/com/juick/server
parent3ea4cd1942fa4e763034da11c5fa429407b67829 (diff)
parenta49105285d0d7719d7f222a507af2d5ac5b4bdb1 (diff)
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src/main/java/com/juick/server')
-rw-r--r--src/main/java/com/juick/server/ActivityPubManager.java7
-rw-r--r--src/main/java/com/juick/server/CommandsManager.java34
-rw-r--r--src/main/java/com/juick/server/KeystoreManager.java9
-rw-r--r--src/main/java/com/juick/server/ServerManager.java120
-rw-r--r--src/main/java/com/juick/server/SignatureManager.java92
-rw-r--r--src/main/java/com/juick/server/WebsocketManager.java174
-rw-r--r--src/main/java/com/juick/server/XMPPManager.java (renamed from src/main/java/com/juick/server/XMPPConnection.java)92
-rw-r--r--src/main/java/com/juick/server/XMPPServer.java429
-rw-r--r--src/main/java/com/juick/server/api/ApiSocialLogin.java70
-rw-r--r--src/main/java/com/juick/server/api/Index.java14
-rw-r--r--src/main/java/com/juick/server/api/Messages.java3
-rw-r--r--src/main/java/com/juick/server/api/Notifications.java21
-rw-r--r--src/main/java/com/juick/server/api/PM.java1
-rw-r--r--src/main/java/com/juick/server/api/Users.java59
-rw-r--r--src/main/java/com/juick/server/api/activity/Profile.java117
-rw-r--r--src/main/java/com/juick/server/api/activity/helpers/ActivityIdDeserializer.java22
-rw-r--r--src/main/java/com/juick/server/api/activity/helpers/LinkValueDeserializer.java21
-rw-r--r--src/main/java/com/juick/server/api/activity/model/Activity.java4
-rw-r--r--src/main/java/com/juick/server/api/activity/model/Context.java3
-rw-r--r--src/main/java/com/juick/server/api/rss/MessagesView.java2
-rw-r--r--src/main/java/com/juick/server/api/webfinger/Resource.java2
-rw-r--r--src/main/java/com/juick/server/api/xnodeinfo2/Info.java40
-rw-r--r--src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java35
-rw-r--r--src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java6
-rw-r--r--src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java3
-rw-r--r--src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java3
-rw-r--r--src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java3
-rw-r--r--src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java8
-rw-r--r--src/main/java/com/juick/server/configuration/ApiAppConfiguration.java29
-rw-r--r--src/main/java/com/juick/server/configuration/BaseWebConfiguration.java18
-rw-r--r--src/main/java/com/juick/server/configuration/SecurityConfig.java40
-rw-r--r--src/main/java/com/juick/server/configuration/XMPPConfig.java34
-rw-r--r--src/main/java/com/juick/server/helpers/HeaderRequestInterceptor.java26
-rw-r--r--src/main/java/com/juick/server/util/ImageUtils.java2
-rw-r--r--src/main/java/com/juick/server/www/VaryHandler.java14
-rw-r--r--src/main/java/com/juick/server/www/controllers/AnythingFilter.java2
-rw-r--r--src/main/java/com/juick/server/www/controllers/MessagesWWW.java42
-rw-r--r--src/main/java/com/juick/server/www/controllers/Settings.java14
-rw-r--r--src/main/java/com/juick/server/www/controllers/SocialLogin.java70
-rw-r--r--src/main/java/com/juick/server/xmpp/XMPPStatusPage.java32
-rw-r--r--src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java48
-rw-r--r--src/main/java/com/juick/server/xmpp/router/Handshake.java39
-rw-r--r--src/main/java/com/juick/server/xmpp/router/Stream.java202
-rw-r--r--src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java57
-rw-r--r--src/main/java/com/juick/server/xmpp/router/StreamError.java57
-rw-r--r--src/main/java/com/juick/server/xmpp/router/StreamFeatures.java95
-rw-r--r--src/main/java/com/juick/server/xmpp/router/StreamHandler.java13
-rw-r--r--src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java10
-rw-r--r--src/main/java/com/juick/server/xmpp/router/XMPPError.java73
-rw-r--r--src/main/java/com/juick/server/xmpp/router/XMPPRouter.java220
-rw-r--r--src/main/java/com/juick/server/xmpp/router/XmlUtils.java88
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java68
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java40
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/Connection.java158
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java231
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java16
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java189
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java65
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java28
-rw-r--r--src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java37
60 files changed, 578 insertions, 2873 deletions
diff --git a/src/main/java/com/juick/server/ActivityPubManager.java b/src/main/java/com/juick/server/ActivityPubManager.java
index 5249f3c9..a5398325 100644
--- a/src/main/java/com/juick/server/ActivityPubManager.java
+++ b/src/main/java/com/juick/server/ActivityPubManager.java
@@ -182,9 +182,10 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
cc.add(replier);
note.setCc(cc);
}
+ subscribers.addAll(note.getCc());
subscribers.forEach(acct -> {
Optional<Context> context = signatureManager.getContext(URI.create(acct));
- if (context.isPresent()) {
+ if (context.isPresent() && context.get() instanceof Person) {
Person follower = (Person)context.get();
Create create = new Create();
create.setId(note.getId());
@@ -268,7 +269,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
note.setTo(Collections.singletonList("https://www.w3.org/ns/activitystreams#Public"));
note.setCc(Collections.singletonList(followersUri(msg.getUser())));
}
- note.setPublished(msg.getTimestamp());
+ note.setPublished(msg.getCreated());
if (StringUtils.isNotBlank(msg.getAttachmentType())) {
Image attachment = new Image();
attachment.setId(msg.getAttachment().getMedium().getUrl());
@@ -304,7 +305,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
Person person = (Person) personContext.get();
note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername()));
List<String> cc = new ArrayList<>(note.getCc());
- cc.add(person.getUrl());
+ cc.add(person.getId());
note.setCc(cc);
}
});
diff --git a/src/main/java/com/juick/server/CommandsManager.java b/src/main/java/com/juick/server/CommandsManager.java
index fa3c5537..f6f29941 100644
--- a/src/main/java/com/juick/server/CommandsManager.java
+++ b/src/main/java/com/juick/server/CommandsManager.java
@@ -25,6 +25,7 @@ import com.juick.model.CommandResult;
import com.juick.model.TagStats;
import com.juick.server.helpers.annotation.UserCommand;
import com.juick.server.util.HttpUtils;
+import com.juick.server.www.WebApp;
import com.juick.service.*;
import com.juick.service.activities.DeleteMessageEvent;
import com.juick.service.component.*;
@@ -34,12 +35,15 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import javax.inject.Inject;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.*;
@@ -53,6 +57,7 @@ import java.util.stream.Collectors;
*/
@Component
public class CommandsManager {
+ private static final Logger logger = LoggerFactory.getLogger(CommandsManager.class);
@Inject
private MessagesService messagesService;
@Inject
@@ -75,6 +80,8 @@ public class CommandsManager {
private ApplicationEventPublisher applicationEventPublisher;
@Inject
private ImagesService imagesService;
+ @Inject
+ private WebApp webApp;
public CommandResult processCommand(@Nonnull User user, String data, @Nonnull URI attachment) throws Exception {
if (!user.isAnonymous()) {
@@ -121,12 +128,16 @@ public class CommandsManager {
: HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost();
attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
}
+ if (StringUtils.isEmpty(body) && !haveAttachment) {
+ return CommandResult.fromString("Empty message");
+ }
int mid = messagesService.createMessage(user.getUid(), body, attachmentType, tags.getRight());
if (haveAttachment) {
String fname = String.format("%d.%s", mid, attachmentType);
imagesService.saveImageWithPreviews(attachmentFName, fname);
}
Message msg = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new);
+ msg.getUser().setAvatar(webApp.getAvatarUrl(msg.getUser()));
subscriptionService.subscribeMessage(msg, user);
applicationEventPublisher.publishEvent(new MessageReadEvent(this, user, msg));
@@ -160,7 +171,7 @@ public class CommandsManager {
String body = arguments[1];
User user_to = userService.getUserByName(arguments[0]);
-
+ user_to.setAvatar(webApp.getAvatarUrl(user_to));
if (!user_to.isAnonymous()) {
if (!userService.isInBLAny(user_to.getUid(), user_from.getUid())) {
if (pmQueriesService.createPM(user_from.getUid(), user_to.getUid(), body)) {
@@ -523,12 +534,22 @@ public class CommandsManager {
String attachmentFName = null;
String attachmentType = null;
if (haveAttachment) {
- attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost()
- : HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost();
- attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
+ if (attachment.getScheme().equals("juick")) {
+ attachmentFName = attachment.getHost();
+ attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
+ } else {
+ try {
+ attachmentFName = HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost();
+ attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
+ } catch (IOException e) {
+ logger.warn("Can not download {}", attachment.toURL());
+ }
+ }
}
- int newrid = messagesService.createReply(mid, rid, user, txt, attachmentType);
- if (haveAttachment) {
+ boolean attachmentProcessed = !haveAttachment || StringUtils.isNotEmpty(attachmentType);
+ String messageText = attachmentProcessed ? txt : String.format("%s %s", txt, attachment.toASCIIString());
+ int newrid = messagesService.createReply(mid, rid, user, messageText, attachmentType);
+ if (haveAttachment && attachmentProcessed) {
String fname = String.format("%d-%d.%s", mid, newrid, attachmentType);
imagesService.saveImageWithPreviews(attachmentFName, fname);
}
@@ -537,6 +558,7 @@ public class CommandsManager {
Message original = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new);
subscriptionService.subscribeMessage(original, user);
Message reply = messagesService.getReply(mid, newrid);
+ reply.getUser().setAvatar(webApp.getAvatarUrl(reply.getUser()));
applicationEventPublisher.publishEvent(new MessageEvent(this, reply, subscriptionService.getUsersSubscribedToComments(original, reply)));
return CommandResult.build(reply,"Reply posted.\n#" + mid + "/" + newrid + " "
+ "https://juick.com/m/" + mid + "#" + newrid,
diff --git a/src/main/java/com/juick/server/KeystoreManager.java b/src/main/java/com/juick/server/KeystoreManager.java
index 67a24f11..3ae7b866 100644
--- a/src/main/java/com/juick/server/KeystoreManager.java
+++ b/src/main/java/com/juick/server/KeystoreManager.java
@@ -19,20 +19,17 @@ import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.stream.Collectors;
-@Component
public class KeystoreManager {
private static final Logger logger = LoggerFactory.getLogger("com.juick.server");
- @Value("${keystore:juick.p12}")
- private String keystore;
- @Value("${keystore_password:secret}")
+
private String keystorePassword;
private KeyStore ks;
private KeyManagerFactory kmf;
- @PostConstruct
- public void init() {
+ public KeystoreManager(String keystore, String keystorePassword) {
+ this.keystorePassword = keystorePassword;
try (InputStream ksIs = new FileInputStream(keystore)) {
ks = KeyStore.getInstance("PKCS12");
ks.load(ksIs, keystorePassword.toCharArray());
diff --git a/src/main/java/com/juick/server/ServerManager.java b/src/main/java/com/juick/server/ServerManager.java
index 7a95ec43..46faf9eb 100644
--- a/src/main/java/com/juick/server/ServerManager.java
+++ b/src/main/java/com/juick/server/ServerManager.java
@@ -16,7 +16,6 @@
*/
package com.juick.server;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.juick.Message;
import com.juick.User;
@@ -24,25 +23,18 @@ import com.juick.model.AnonymousUser;
import com.juick.service.MessagesService;
import com.juick.service.SubscriptionService;
import com.juick.service.UserService;
-import com.juick.service.component.LikeEvent;
-import com.juick.service.component.MessageEvent;
-import com.juick.service.component.MessageReadEvent;
-import com.juick.service.component.NotificationListener;
-import com.juick.service.component.PingEvent;
-import com.juick.service.component.SubscribeEvent;
-import com.juick.service.component.TopEvent;
+import com.juick.service.component.*;
import com.juick.util.MessageUtils;
+import org.apache.commons.collections4.ListUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-import org.springframework.web.socket.TextMessage;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -62,8 +54,6 @@ public class ServerManager implements NotificationListener {
@Inject
private MessagesService messagesService;
@Inject
- private WebsocketManager wsHandler;
- @Inject
private SubscriptionService subscriptionService;
@Inject
private UserService userService;
@@ -81,96 +71,15 @@ public class ServerManager implements NotificationListener {
private void onJuickPM(final User to, final com.juick.Message jmsg) {
- try {
- String json = jsonMapper.writeValueAsString(jmsg);
- synchronized (wsHandler.getClients()) {
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor.getUid() == to.getUid()) || c.visitor.equals(serviceUser))
- .forEach(c -> {
- try {
- logger.debug("sending pm to {}", c.visitor.getUid());
- c.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- }
- } catch (JsonProcessingException e) {
- logger.warn("Invalid JSON", e);
- }
- messageEvent(jmsg, Collections.singletonList(to));
+ messageEvent(jmsg, Arrays.asList(to, jmsg.getUser()));
}
private void onJuickMessagePost(final com.juick.Message jmsg, List<User> subscribedUsers) {
- try {
- String json = jsonMapper.writeValueAsString(jmsg);
- List<Integer> uids = subscribedUsers
- .stream().map(User::getUid).collect(Collectors.toList());
- synchronized (wsHandler.getClients()) {
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor.isAnonymous()) // anonymous users
- || c.visitor.equals(serviceUser) // services
- || (!c.legacy && uids.contains(c.visitor.getUid()))) // subscriptions
- .forEach(c -> {
- try {
- logger.debug("sending message to {}", c.visitor.getUid());
- c.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- wsHandler.getClients().stream().filter(c ->
- c.legacy && c.allMessages) // legacy all posts
- .forEach(c -> {
- try {
- logger.debug("sending message to legacy client {}", c.visitor.getUid());
- c.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- }
- } catch (JsonProcessingException e) {
- logger.warn("Invalid JSON", e);
- }
messageEvent(jmsg, subscribedUsers);
messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE));
}
private void onJuickMessageReply(final com.juick.Message jmsg, final List<User> subscribedUsers) {
- try {
-
- String json = jsonMapper.writeValueAsString(jmsg);
- List<Integer> threadUsers =
- subscribedUsers
- .stream().map(User::getUid).collect(Collectors.toList());
- synchronized (wsHandler.getClients()) {
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor.isAnonymous()) // anonymous users
- || c.visitor.equals(serviceUser) // services
- || (!c.legacy && threadUsers.contains(c.visitor.getUid()))) // subscriptions
- .forEach(c -> {
- try {
- logger.debug("sending reply to {}", c.visitor.getUid());
- c.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- wsHandler.getClients().stream().filter(c ->
- (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMid())) // legacy replies
- .forEach(c -> {
- try {
- logger.debug("sending reply to legacy client {}", c.visitor.getUid());
- c.sendMessage(new TextMessage(json));
- } catch (IOException e) {
- logger.warn("ws error", e);
- }
- });
- }
- } catch (JsonProcessingException e) {
- logger.warn("Invalid JSON", e);
- }
messageEvent(jmsg, subscribedUsers);
messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE));
}
@@ -178,7 +87,7 @@ public class ServerManager implements NotificationListener {
@Override
public void processMessageEvent(MessageEvent event) {
com.juick.Message jmsg = event.getMessage();
- List<User> subscribedUsers = event.getUsers();
+ List<User> subscribedUsers = ListUtils.union(event.getUsers(), Collections.singletonList(jmsg.getUser()));
if (jmsg.isService()) {
readEvent(jmsg, Collections.singletonList(serviceUser));
return;
@@ -186,16 +95,14 @@ public class ServerManager implements NotificationListener {
if (MessageUtils.isPM(jmsg)) {
onJuickPM(jmsg.getTo(), jmsg);
} else if (!MessageUtils.isReply(jmsg)) {
- // to get full message with attachment, etc.
- onJuickMessagePost(messagesService.getMessage(jmsg.getMid()).orElseThrow(IllegalStateException::new), subscribedUsers);
+ onJuickMessagePost(jmsg, subscribedUsers);
} else {
// to get quote and attachment
Message op = messagesService.getMessage(jmsg.getMid()).orElseThrow(IllegalStateException::new);
- com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
- subscriptionService.getUsersSubscribedToComments(op, reply, true).stream()
- .filter(u -> userService.isReplyToBL(u, reply))
- .forEach(b -> messagesService.setLastReadComment(b, reply.getMid(), reply.getRid()));
- onJuickMessageReply(reply, subscribedUsers);
+ subscriptionService.getUsersSubscribedToComments(op, jmsg, true).stream()
+ .filter(u -> userService.isReplyToBL(u, jmsg))
+ .forEach(b -> messagesService.setLastReadComment(b, jmsg.getMid(), jmsg.getRid()));
+ onJuickMessageReply(jmsg, subscribedUsers);
}
messageEvent(jmsg, Collections.singletonList(serviceUser));
}
@@ -226,15 +133,6 @@ public class ServerManager implements NotificationListener {
serviceMessage.setUser(user);
serviceMessage.setMid(source.getMid());
serviceMessage.setUnread(false);
- wsHandler.getClients().stream().filter(c ->
- (!c.legacy && c.visitor == user) || c.visitor.equals(serviceUser)
- ).forEach(u -> {
- try {
- u.sendMessage(new TextMessage(jsonMapper.writeValueAsString(serviceMessage)));
- } catch (IOException e) {
- logger.error("JSON error", e);
- }
- });
readEvent(serviceMessage, Collections.singletonList(serviceUser));
}
diff --git a/src/main/java/com/juick/server/SignatureManager.java b/src/main/java/com/juick/server/SignatureManager.java
index b3b7a301..755575ce 100644
--- a/src/main/java/com/juick/server/SignatureManager.java
+++ b/src/main/java/com/juick/server/SignatureManager.java
@@ -1,14 +1,17 @@
package com.juick.server;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.juick.User;
+import com.juick.model.AnonymousUser;
import com.juick.server.api.activity.model.Context;
import com.juick.server.api.activity.model.objects.Person;
import com.juick.server.api.webfinger.model.Account;
import com.juick.server.api.webfinger.model.Link;
+import com.juick.service.UserService;
import com.juick.util.DateFormattersHolder;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
@@ -41,7 +44,7 @@ public class SignatureManager {
@Inject
private ObjectMapper jsonMapper;
@Inject
- private ApplicationEventPublisher applicationEventPublisher;
+ private UserService userService;
@Inject
private RestTemplate apClient;
@@ -50,56 +53,89 @@ public class SignatureManager {
URI inbox = uriComponentsBuilder.build().toUri();
Instant now = Instant.now();
String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now);
- Signature templateSignature = new Signature(from.getPublicKey().getId(), "rsa-sha256", null,
- "(request-target)", "host", "date");
- Signer signer = new Signer(keystoreManager.getPrivateKey(), templateSignature);
- Map<String, String> headers = new HashMap<>();
- headers.put("host", inbox.getHost());
- headers.put("date", requestDate);
- Signature signature = signer.sign("POST", inbox.getPath(), headers);
+ String host = inbox.getPort() > 0 ? String.format("%s:%d", inbox.getHost(), inbox.getPort()) : inbox.getHost();
+ String signatureString = addSignature(from, host, "POST", inbox.getPath(), requestDate);
+
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Content-Type", Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE);
requestHeaders.add("Date", requestDate);
- requestHeaders.add("Signature", signature.toString().substring(10));
+ requestHeaders.add("Host", host);
+ requestHeaders.add("Signature", signatureString);
HttpEntity<Context> request = new HttpEntity<>(Context.build(data), requestHeaders);
- //boolean valid = verifySignature(Signature.fromString(requestHeaders.getFirst("Signature")),
- // keystoreManager.getPublicKey(), "POST", inbox.getPath(), headers);
logger.info("Sending context: {}", jsonMapper.writeValueAsString(data));
logger.info("Request date: {}", requestDate);
ResponseEntity<Void> response = apClient.postForEntity(inbox, request, Void.class);
logger.info("accepted follower: {}", response.getStatusCodeValue());
+ }
+ public String addSignature(Person from, String host, String method, String path, String dateString) throws IOException {
+ return addSignature(from, host, method, path, dateString, keystoreManager);
}
- public boolean verifySignature(String signatureString, URI actor, String method, String path, Map<String, String> headers) {
- Optional<Context> context = getContext(actor);
+
+ public String addSignature(Person from, String host, String method, String path, String dateString, KeystoreManager keystoreManager) throws IOException {
+ Signature templateSignature = new Signature(from.getPublicKey().getId(), "rsa-sha256", null,
+ "(request-target)", "host", "date");
+ Map<String, String> headers = new HashMap<>();
+ headers.put("host", host);
+ headers.put("date", dateString);
+ Signer signer = new Signer(keystoreManager.getPrivateKey(), templateSignature);
+ Signature signature = signer.sign(method, path, headers);
+ // remove "Signature: " from result
+ return signature.toString().substring(10);
+ }
+
+ public User verifySignature(String method, String path, Map<String, String> headers) {
+ String signatureString = headers.get("signature");
+ logger.info("Signature: {}", signatureString);
+ Signature signature = Signature.fromString(signatureString);
+ Optional<Context> context = getContext(UriComponentsBuilder.fromUriString(signature.getKeyId())
+ .fragment(null).build().toUri());
if (context.isPresent() && context.get() instanceof Person) {
Person person = (Person) context.get();
Key key = KeystoreManager.publicKeyOf(person);
- Verifier verifier = new Verifier(key, Signature.fromString(signatureString));
+
+ Verifier verifier = new Verifier(key, signature);
try {
boolean result = verifier.verify(method, path, headers);
- logger.info("signature is valid: {}", result);
- return result;
+ logger.info("signature of {} is valid: {}", signature.getKeyId(), result);
+ if (result) {
+ User user = new User();
+ user.setUri(URI.create(person.getId()));
+ if (key.equals(keystoreManager.getPublicKey())) {
+ return userService.getUserByName(person.getName());
+ }
+ return user;
+ } else {
+ return AnonymousUser.INSTANCE;
+ }
} catch (NoSuchAlgorithmException | SignatureException | IOException e) {
- logger.info("signature exception", e);
- return false;
+ logger.warn("Invalid signature {}", signatureString);
}
+ } else {
+ logger.warn("Unknown keyId");
}
- logger.info("person not found");
- return false;
+ return AnonymousUser.INSTANCE;
}
public Optional<Context> getContext(URI contextUri) {
- Context context = apClient.getForEntity(contextUri, Context.class).getBody();
- if (context == null) {
- logger.warn("Cannot identify {}", contextUri);
- return Optional.empty();
+ try {
+ Context context = apClient.getForEntity(contextUri, Context.class).getBody();
+ if (context == null) {
+ logger.warn("Cannot identify {}", contextUri);
+ return Optional.empty();
+ }
+ return Optional.of(context);
+ } catch (Exception e) {
+ logger.warn("REST Exception on {}: {}", contextUri, e.getMessage());
}
- return Optional.of(context);
+ return Optional.empty();
}
public Optional<Context> discoverPerson(String acct) {
- Jid acctId = Jid.of(acct);
+ String[] accountParts = acct.split(":", 2);
+ String account = accountParts[0];
+ int port = accountParts.length > 1 ? Integer.valueOf(accountParts[1]) : 80;
+ Jid acctId = Jid.of(account);
URI resourceUri = UriComponentsBuilder.fromUriString(
- String.format("https://%s/.well-known/webfinger?resource=acct:%s", acctId.getDomain(), acct)).build().toUri();
+ String.format("http://%s:%d/.well-known/webfinger?resource=acct:%s", acctId.getDomain(), port, account)).build().toUri();
Account acctData = apClient.getForEntity(resourceUri, Account.class).getBody();
if (acctData != null) {
for (Link l : acctData.getLinks()) {
diff --git a/src/main/java/com/juick/server/WebsocketManager.java b/src/main/java/com/juick/server/WebsocketManager.java
deleted file mode 100644
index 1b62b984..00000000
--- a/src/main/java/com/juick/server/WebsocketManager.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server;
-
-import com.juick.User;
-import com.juick.model.AnonymousUser;
-import com.juick.model.CommandResult;
-import com.juick.server.util.HttpForbiddenException;
-import com.juick.server.util.HttpNotFoundException;
-import com.juick.service.MessagesService;
-import com.juick.service.UserService;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpHeaders;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.web.socket.CloseStatus;
-import org.springframework.web.socket.PingMessage;
-import org.springframework.web.socket.TextMessage;
-import org.springframework.web.socket.WebSocketSession;
-import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;
-import org.springframework.web.socket.handler.TextWebSocketHandler;
-import org.springframework.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URI;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Created by vitalyster on 28.06.2016.
- */
-@Component
-public class WebsocketManager extends TextWebSocketHandler {
- private static final Logger logger = LoggerFactory.getLogger(WebsocketManager.class);
-
- private final List<UserSession> clients = new CopyOnWriteArrayList<>();
-
- @Inject
- private UserService userService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private CommandsManager commandsManager;
-
-
- @Override
- public void afterConnectionEstablished(WebSocketSession session) {
-
- UserSession userSession = new UserSession(session);
- URI hLocation = session.getUri();
-
- // Auth
- UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build();
- List<String> hash = uriComponents.getQueryParams().get("hash");
- if (hash != null && hash.get(0).length() == 16) {
- userSession.visitor = userService.getUserByHash(hash.get(0));
- } else {
- logger.debug("wrong hash for {} from {}", userSession.visitor.getUid(), userSession);
- }
-
- if (hLocation.getPath().equals("/ws/")) {
- logger.debug("user {} connected", userSession.visitor.getUid());
- } else if (hLocation.getPath().equals("/ws/_all")) {
- logger.debug("user {} connected to legacy _all ({})", userSession.visitor.getUid(), hLocation.getPath());
- userSession.legacy = true;
- userSession.allMessages = true;
- } else if (hLocation.getPath().equals("/ws/_replies")) {
- logger.debug("user {} connected to legacy _replies ({})", userSession.visitor.getUid(), hLocation.getPath());
- userSession.legacy = true;
- userSession.allReplies = true;
- } else if (hLocation.getPath().matches("^/ws/(\\d)+$")) {
- int MID = NumberUtils.toInt(hLocation.getPath().substring(4), 0);
- if (MID > 0) {
- if (messagesService.canViewThread(MID, userSession.visitor.getUid())) {
- logger.debug("user {} connected to legacy thread ({}) from {}", userSession.visitor.getUid(), MID, userSession);
- userSession.legacy = true;
- userSession.MID = MID;
- } else {
- throw new HttpForbiddenException();
- }
- }
- } else {
- throw new HttpNotFoundException();
- }
- clients.add(userSession);
- logger.debug("{} clients connected", clients.size());
- }
-
- @Override
- public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
- logger.debug("session closed with status {}: {}", status.getCode(), status.getReason());
- clients.removeIf(c -> c.getDelegate().getId().equals(session.getId()));
- logger.debug("{} clients connected", clients.size());
- }
-
- @Scheduled(fixedRate = 30000)
- public void ping() {
- clients.forEach(c -> {
- try {
- if (c.isOpen()) {
- c.sendMessage(new PingMessage());
- }
- } catch (IOException e) {
- logger.error("WebSocket PING exception", e);
- }
- });
- }
-
- @Override
- protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
- UserSession ws = clients.stream().filter(c -> c.getDelegate().equals(session))
- .findFirst().orElseThrow(IllegalStateException::new);
- if (!ws.visitor.isAnonymous()) {
- String command = message.getPayload().trim();
- if (StringUtils.isNotEmpty(command)) {
- CommandResult result = commandsManager.processCommand(ws.visitor, command, URI.create(""));
- ws.sendMessage(new TextMessage(result.getText()));
- }
- } else {
- ws.sendMessage(new TextMessage("Authorization required"));
- }
- }
-
- public List<UserSession> getClients() {
- return clients;
- }
-
- class UserSession extends ConcurrentWebSocketSessionDecorator {
- User visitor;
- int MID;
- boolean allMessages;
- boolean allReplies;
- Instant tsConnected;
- Instant tsLastData;
- boolean legacy;
-
- UserSession(WebSocketSession session) {
- super(session, 60000, 65536);
- this.visitor = AnonymousUser.INSTANCE;
- tsConnected = tsLastData = Instant.now();
- }
-
- @Nonnull
- @Override
- public String toString() {
- HttpHeaders headers = getHandshakeHeaders();
- return headers.getOrDefault("X-Real-IP",
- Collections.singletonList(getRemoteAddress().toString())).get(0);
- }
- }
-}
diff --git a/src/main/java/com/juick/server/XMPPConnection.java b/src/main/java/com/juick/server/XMPPManager.java
index f77b2354..51b3b04e 100644
--- a/src/main/java/com/juick/server/XMPPConnection.java
+++ b/src/main/java/com/juick/server/XMPPManager.java
@@ -19,14 +19,13 @@ package com.juick.server;
import com.juick.User;
import com.juick.formatters.PlainTextFormatter;
-import com.juick.server.www.WebApp;
-import com.juick.service.component.*;
import com.juick.model.CommandResult;
-import com.juick.model.UserInfo;
+import com.juick.server.www.WebApp;
import com.juick.server.xmpp.iq.MessageQuery;
-import com.juick.server.xmpp.s2s.BasicXmppSession;
-import com.juick.server.xmpp.s2s.StanzaListener;
-import com.juick.service.*;
+import com.juick.service.MessagesService;
+import com.juick.service.PMQueriesService;
+import com.juick.service.UserService;
+import com.juick.service.component.*;
import com.juick.util.MessageUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
@@ -35,12 +34,17 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
import rocks.xmpp.addr.Jid;
import rocks.xmpp.core.XmppException;
+import rocks.xmpp.core.session.Extension;
import rocks.xmpp.core.session.XmppSession;
+import rocks.xmpp.core.session.XmppSessionConfiguration;
+import rocks.xmpp.core.session.debug.LogbackDebugger;
import rocks.xmpp.core.stanza.AbstractIQHandler;
-import rocks.xmpp.core.stanza.model.*;
+import rocks.xmpp.core.stanza.model.IQ;
+import rocks.xmpp.core.stanza.model.Message;
+import rocks.xmpp.core.stanza.model.Presence;
+import rocks.xmpp.core.stanza.model.StanzaError;
import rocks.xmpp.core.stanza.model.client.ClientMessage;
import rocks.xmpp.core.stanza.model.client.ClientPresence;
import rocks.xmpp.core.stanza.model.errors.Condition;
@@ -57,23 +61,19 @@ import rocks.xmpp.extensions.receipts.MessageDeliveryReceiptsManager;
import rocks.xmpp.extensions.vcard.temp.model.VCard;
import rocks.xmpp.extensions.version.SoftwareVersionManager;
import rocks.xmpp.extensions.version.model.SoftwareVersion;
-import rocks.xmpp.util.XmppUtils;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
-import javax.xml.bind.JAXBException;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
import java.io.IOException;
-import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.Duration;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -83,14 +83,12 @@ import java.util.concurrent.ExecutorService;
/**
* @author ugnich
*/
-public class XMPPConnection implements StanzaListener, NotificationListener {
+public class XMPPManager implements NotificationListener {
private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp");
private ExternalComponent router;
@Inject
- private XMPPServer xmpp;
- @Inject
private CommandsManager commandsManager;
@Value("${xmppbot_jid:juick@localhost}")
private Jid jid;
@@ -98,6 +96,8 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
private String componentName;
@Value("${component_port:5347}")
private int componentPort;
+ @Value("${component_host:localhost}")
+ private String componentHost;
@Value("${xmpp_password:secret}")
private String password;
@Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
@@ -110,9 +110,7 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
@Inject
private PMQueriesService pmQueriesService;
@Inject
- private BasicXmppSession session;
- @Inject
- private ExecutorService service;
+ private ExecutorService executorService;
@Value("${service_user:juick}")
private String serviceUsername;
@Inject
@@ -123,8 +121,12 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
@PostConstruct
public void init() {
logger.info("stream router start connecting to {}", componentPort);
- xmpp.addStanzaListener(this);
- router = ExternalComponent.create(componentName, password, session.getConfiguration(), "localhost", componentPort);
+ XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
+ .extensions(Extension.of(com.juick.Message.class), Extension.of(MessageQuery.class))
+ .debugger(LogbackDebugger.class)
+ .defaultResponseTimeout(Duration.ofMillis(120000))
+ .build();
+ router = ExternalComponent.create(componentName, password, configuration, componentHost, componentPort);
ServiceDiscoveryManager serviceDiscoveryManager = router.getManager(ServiceDiscoveryManager.class);
serviceDiscoveryManager.addIdentity(Identity.clientBot().withName("Juick"));
EntityCapabilitiesManager entityCapabilitiesManager = router.getManager(EntityCapabilitiesManager.class);
@@ -164,7 +166,7 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
}
User user = userService.getUserByName(iq.getTo().getLocal());
if (!user.isAnonymous()) {
- UserInfo info = userService.getUserInfo(user);
+ User info = userService.getUserInfo(user);
VCard userVCard = new VCard();
userVCard.setFormattedName(info.getFullName());
userVCard.setNickname(user.getName());
@@ -187,13 +189,6 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
router.send(result);
}
});
- router.addInboundIQListener(e -> {
- IQ iq = e.getIQ();
- Jid jid = iq.getTo();
- if (!jid.getDomain().equals(this.jid.getDomain())) {
- router.send(iq);
- }
- });
FileTransferManager fileTransferManager = router.getManager(FileTransferManager.class);
fileTransferManager.addFileTransferOfferListener(e -> {
try {
@@ -261,7 +256,7 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
router.addInboundPresenceListener(event -> {
incomingPresence(event.getPresence());
});
- service.submit(() -> {
+ executorService.submit(() -> {
try {
router.connect();
} catch (XmppException e) {
@@ -271,16 +266,6 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
serviceUser = userService.getUserByName(serviceUsername);
}
- private String stanzaToString(Stanza stanza) throws XMLStreamException, JAXBException {
- StringWriter stanzaWriter = new StringWriter();
- XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter(
- router.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter));
- router.createMarshaller().marshal(stanza, xmppStreamWriter);
- xmppStreamWriter.flush();
- xmppStreamWriter.close();
- return stanzaWriter.toString();
- }
-
private void sendJuickMessage(com.juick.Message jmsg, List<User> users) {
List<String> jids = new ArrayList<>();
@@ -319,7 +304,7 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
}
}
- public void sendJuickComment(com.juick.Message jmsg, List<User> users) {
+ private void sendJuickComment(com.juick.Message jmsg, List<User> users) {
String replyQuote;
String replyTo;
@@ -569,7 +554,6 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
}
public ClientMessage incomingMessage(Message msg) {
- ClientMessage result = null;
if (msg.getType() != null && msg.getType().equals(Message.Type.ERROR)) {
StanzaError error = msg.getError();
if (error != null && error.getCondition().equals(Condition.RESOURCE_CONSTRAINT)) {
@@ -598,19 +582,10 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
} catch (Exception e1) {
logger.warn("message exception", e1);
}
- } else if (to.getDomain().endsWith(jid.getDomain()) && (to.getDomain().equals(jid.getDomain())
- || to.getDomain().endsWith("." + jid.getDomain()))) {
- if (logger.isInfoEnabled()) {
- try {
- logger.info("unhandled message: {}", stanzaToString(msg));
- } catch (JAXBException | XMLStreamException ex) {
- logger.error("JAXB exception", ex);
- }
- }
- } else {
- return ClientMessage.from(msg);
}
- return result;
+ ClientMessage errorMessage = ClientMessage.from(msg);
+ errorMessage.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND));
+ return errorMessage;
}
private ClientMessage incomingMessageJuick(User user_from, Jid from, String to, String command, @Nonnull URI attachment) {
if (StringUtils.isBlank(command) && attachment.toString().isEmpty()) {
@@ -646,11 +621,6 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
return null;
}
- @Override
- public void stanzaReceived(Stanza xmlValue) {
- router.send(xmlValue);
- }
-
private void broadcastPresence(Presence.Type type) {
Presence presence = new Presence();
presence.setFrom(jid);
@@ -674,8 +644,4 @@ public class XMPPConnection implements StanzaListener, NotificationListener {
router.close();
}
}
-
- public ExternalComponent getRouter() {
- return router;
- }
}
diff --git a/src/main/java/com/juick/server/XMPPServer.java b/src/main/java/com/juick/server/XMPPServer.java
deleted file mode 100644
index 86ab6a78..00000000
--- a/src/main/java/com/juick/server/XMPPServer.java
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server;
-
-import com.juick.server.xmpp.router.StreamError;
-import com.juick.server.xmpp.s2s.*;
-import com.juick.service.UserService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.xmlpull.v1.XmlPullParserException;
-import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.stanza.model.Stanza;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.net.ssl.*;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.SecureRandom;
-import java.security.cert.*;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * @author ugnich
- */
-public class XMPPServer implements ConnectionListener {
- private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp");
-
- private static final int TIMEOUT_MINUTES = 15;
-
- @Inject
- public ExecutorService service;
- @Value("${hostname:localhost}")
- private Jid jid;
- @Value("${s2s_port:5269}")
- private int s2sPort;
- @Value("${broken_ssl_hosts:}")
- public String[] brokenSSLhosts;
- @Value("${banned_hosts:}")
- public String[] bannedHosts;
-
- private final List<ConnectionIn> inConnections = new CopyOnWriteArrayList<>();
- private final Map<ConnectionOut, Optional<Socket>> outConnections = new ConcurrentHashMap<>();
- private final List<CacheEntry> outCache = new CopyOnWriteArrayList<>();
- private final List<StanzaListener> stanzaListeners = new CopyOnWriteArrayList<>();
- private final AtomicBoolean closeFlag = new AtomicBoolean(false);
-
- SSLContext sc;
- CertificateFactory cf;
- CertPathValidator cpv;
- PKIXParameters params;
- private TrustManager[] trustAllCerts = new TrustManager[]{
- new X509TrustManager() {
- public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
- }
-
- public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
- }
-
- public java.security.cert.X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- }
- };
- private boolean tlsConfigured = false;
-
-
- private ServerSocket listener;
-
- @Inject
- private BasicXmppSession session;
- @Inject
- private UserService userService;
- @Inject
- private KeystoreManager keystoreManager;
-
- @PostConstruct
- public void init() throws KeyStoreException {
- closeFlag.set(false);
- try {
- sc = SSLContext.getInstance("TLSv1.2");
- sc.init(keystoreManager.getKeymanagerFactory().getKeyManagers(), trustAllCerts, new SecureRandom());
- TrustManagerFactory trustManagerFactory =
- TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- Set<TrustAnchor> ca = new HashSet<>();
- trustManagerFactory.init((KeyStore)null);
- Arrays.stream(trustManagerFactory.getTrustManagers()).forEach(t -> Arrays.stream(((X509TrustManager)t).getAcceptedIssuers()).forEach(cert -> ca.add(new TrustAnchor(cert, null))));
- params = new PKIXParameters(ca);
- params.setRevocationEnabled(false);
- cpv = CertPathValidator.getInstance("PKIX");
- cf = CertificateFactory.getInstance( "X.509" );
- tlsConfigured = true;
- } catch (Exception e) {
- logger.warn("tls unavailable");
- }
- service.submit(() -> {
- try {
- listener = new ServerSocket(s2sPort);
- logger.info("s2s listener ready");
- while (!listener.isClosed()) {
- if (Thread.currentThread().isInterrupted()) break;
- Socket socket = listener.accept();
- ConnectionIn client = new ConnectionIn(this, socket);
- addConnectionIn(client);
- service.submit(client);
- }
- } catch (SocketException e) {
- // shutdown
- } catch (IOException | XmlPullParserException e) {
- logger.warn("xmpp exception", e);
- }
- });
- }
-
- public void addConnectionIn(ConnectionIn c) {
- c.setListener(this);
- inConnections.add(c);
- }
-
- public void addConnectionOut(ConnectionOut c, Optional<Socket> socket) {
- c.setListener(this);
- outConnections.put(c, socket);
- }
-
- public void removeConnectionIn(ConnectionIn c) {
- inConnections.remove(c);
- }
-
- public void removeConnectionOut(ConnectionOut c) {
- outConnections.remove(c);
- }
-
- public String getFromCache(Jid to) {
- final String[] cache = new String[1];
- outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> {
- cache[0] = c.xml;
- outCache.remove(c);
- });
- return cache[0];
- }
-
- public Optional<ConnectionOut> getConnectionOut(Jid hostname, boolean needReady) {
- return outConnections.keySet().stream().filter(c -> c.to != null &&
- c.to.equals(hostname) && (!needReady || c.streamReady)).findFirst();
- }
-
- public Optional<ConnectionIn> getConnectionIn(String streamID) {
- return inConnections.stream().filter(c -> c.streamID != null && c.streamID.equals(streamID)).findFirst();
- }
-
- public void sendOut(Jid hostname, String xml) {
- boolean haveAnyConn = false;
-
- ConnectionOut connOut = null;
- for (ConnectionOut c : outConnections.keySet()) {
- if (c.to != null && c.to.equals(hostname)) {
- if (c.streamReady) {
- connOut = c;
- break;
- } else {
- haveAnyConn = true;
- break;
- }
- }
- }
- if (connOut != null) {
- connOut.send(xml);
- return;
- }
-
- boolean haveCache = false;
- for (CacheEntry c : outCache) {
- if (c.hostname != null && c.hostname.equals(hostname)) {
- c.xml += xml;
- c.updated = Instant.now();
- haveCache = true;
- break;
- }
- }
- if (!haveCache) {
- outCache.add(new CacheEntry(hostname, xml));
- }
-
- if (!haveAnyConn && !closeFlag.get()) {
- try {
- createDialbackConnection(hostname.toEscapedString(), null, null);
- } catch (Exception e) {
- logger.warn("dialback error", e);
- }
- }
- }
-
- void createDialbackConnection(String to, String checkSID, String dbKey) throws Exception {
- ConnectionOut connectionOut = new ConnectionOut(getJid(), Jid.of(to), null, null, checkSID, dbKey);
- addConnectionOut(connectionOut, Optional.empty());
- service.submit(() -> {
- try {
- Socket socket = new Socket();
- socket.connect(DNSQueries.getServerAddress(to));
- connectionOut.setInputStream(socket.getInputStream());
- connectionOut.setOutputStream(socket.getOutputStream());
- addConnectionOut(connectionOut, Optional.of(socket));
- connectionOut.connect();
- } catch (IOException e) {
- logger.info("dialback to " + to + " exception", e);
- }
- });
- }
-
- public void startDialback(Jid from, String streamId, String dbKey) throws Exception {
- Optional<ConnectionOut> c = getConnectionOut(from, false);
- if (c.isPresent()) {
- c.get().sendDialbackVerify(streamId, dbKey);
- } else {
- createDialbackConnection(from.toEscapedString(), streamId, dbKey);
- }
- }
-
- public void addStanzaListener(StanzaListener listener) {
- stanzaListeners.add(listener);
- }
-
- public void onStanzaReceived(String xmlValue) {
- logger.info("S2S: {}", xmlValue);
- Stanza stanza = parse(xmlValue);
- stanzaListeners.forEach(l -> l.stanzaReceived(stanza));
- }
-
- public BasicXmppSession getSession() {
- return session;
- }
-
- public List<ConnectionIn> getInConnections() {
- return inConnections;
- }
-
- public Map<ConnectionOut, Optional<Socket>> getOutConnections() {
- return outConnections;
- }
-
- @Override
- public boolean isTlsAvailable() {
- return tlsConfigured;
- }
-
- @Override
- public void starttls(ConnectionIn connection) {
- logger.debug("stream {} securing", connection.streamID);
- connection.sendStanza("<proceed xmlns=\"" + Connection.NS_TLS + "\" />");
- try {
- connection.setSocket(sc.getSocketFactory().createSocket(connection.getSocket(), connection.getSocket().getInetAddress().getHostAddress(),
- connection.getSocket().getPort(), false));
- SSLSocket sslSocket = (SSLSocket) connection.getSocket();
- sslSocket.addHandshakeCompletedListener(handshakeCompletedEvent -> {
- try {
- CertPath certPath = cf.generateCertPath(Arrays.asList(handshakeCompletedEvent.getPeerCertificates()));
- cpv.validate(certPath, params);
- connection.setTrusted(true);
- logger.info("connection from {} is trusted", connection.from);
- } catch (SSLPeerUnverifiedException | CertificateException | CertPathValidatorException | InvalidAlgorithmParameterException e) {
- logger.info("connection from {} is NOT trusted, falling back to dialback", connection.from);
- }
- });
- sslSocket.setUseClientMode(false);
- sslSocket.setNeedClientAuth(true);
- sslSocket.startHandshake();
- connection.setSecured(true);
- logger.debug("stream from {} secured", connection.streamID);
- connection.restartParser();
- } catch (XmlPullParserException | IOException sex) {
- logger.warn("stream {} ssl error {}", connection.streamID, sex);
- connection.sendStanza("<failure xmlns=\"" + Connection.NS_TLS + "\" />");
- removeConnectionIn(connection);
- connection.closeConnection();
- }
- }
-
- @Override
- public void proceed(ConnectionOut connection) {
- try {
- Socket socket = outConnections.get(connection).get();
- socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(),
- socket.getPort(), false);
- SSLSocket sslSocket = (SSLSocket) socket;
- sslSocket.addHandshakeCompletedListener(handshakeCompletedEvent -> {
- try {
- CertPath certPath = cf.generateCertPath(Arrays.asList(handshakeCompletedEvent.getPeerCertificates()));
- cpv.validate(certPath, params);
- connection.setTrusted(true);
- logger.info("connection to {} is trusted", connection.to);
- } catch (SSLPeerUnverifiedException | CertificateException | CertPathValidatorException | InvalidAlgorithmParameterException e) {
- logger.info("connection to {} is NOT trusted, falling back to dialback", connection.to);
- }
- });
- sslSocket.setNeedClientAuth(true);
- sslSocket.startHandshake();
- connection.setSecured(true);
- logger.debug("stream to {} secured", connection.getStreamID());
- connection.setInputStream(socket.getInputStream());
- connection.setOutputStream(socket.getOutputStream());
- connection.restartStream();
- connection.sendOpenStream();
- } catch (NoSuchElementException | XmlPullParserException | IOException sex) {
- logger.error("s2s ssl error: {} {}, error {}", connection.to, connection.getStreamID(), sex);
- connection.send("<failure xmlns=\"" + Connection.NS_TLS + "\" />");
- removeConnectionOut(connection);
- connection.logoff();
- }
- }
-
- @Override
- public void verify(ConnectionOut connection, String from, String type, String sid) {
- if (from != null && from.equals(connection.to.toEscapedString()) && sid != null && !sid.isEmpty() && type != null) {
- getConnectionIn(sid).ifPresent(c -> c.sendDialbackResult(Jid.of(from), type));
- }
- }
-
- @Override
- public void dialbackError(ConnectionOut connection, StreamError error) {
- logger.warn("Stream error from {}: {}", connection.getStreamID(), error.getCondition());
- removeConnectionOut(connection);
- connection.logoff();
- }
-
- @Override
- public void finished(ConnectionOut connection, boolean dirty) {
- logger.warn("stream to {} {} finished, dirty={}", connection.to, connection.getStreamID(), dirty);
- removeConnectionOut(connection);
- connection.logoff();
- }
-
- @Override
- public void exception(ConnectionOut connection, Exception ex) {
- logger.error("s2s out exception: {} {}, exception {}", connection.to, connection.getStreamID(), ex);
- removeConnectionOut(connection);
- connection.logoff();
- }
-
- @Override
- public void ready(ConnectionOut connection) {
- logger.debug("stream to {} {} ready", connection.to, connection.getStreamID());
- String cache = getFromCache(connection.to);
- if (cache != null) {
- logger.debug("stream to {} {} sending cache", connection.to, connection.getStreamID());
- connection.send(cache);
- }
- }
-
- @Override
- public boolean securing(ConnectionOut connection) {
- return tlsConfigured && !Arrays.asList(brokenSSLhosts).contains(connection.to.toEscapedString());
- }
-
- public Stanza parse(String xml) {
- try {
- Unmarshaller unmarshaller = session.createUnmarshaller();
- return (Stanza)unmarshaller.unmarshal(new StringReader(xml));
- } catch (JAXBException e) {
- logger.error("JAXB exception", e);
- }
- return null;
- }
-
- public Jid getJid() {
- return jid;
- }
- @Scheduled(fixedDelay = 10000)
- public void cleanUp() {
- Instant now = Instant.now();
- outConnections.keySet().stream().filter(c -> Duration.between(c.getUpdated(), now).toMinutes() > TIMEOUT_MINUTES)
- .forEach(c -> {
- logger.info("closing idle outgoing connection to {}", c.to);
- c.logoff();
- outConnections.remove(c);
- });
-
- inConnections.stream().filter(c -> Duration.between(c.updated, now).toMinutes() > TIMEOUT_MINUTES)
- .forEach(c -> {
- logger.info("closing idle incoming connection from {}", c.from);
- c.closeConnection();
- inConnections.remove(c);
- });
- }
- @PreDestroy
- public void preDestroy() throws IOException {
- closeFlag.set(true);
- if (listener != null && !listener.isClosed()) {
- listener.close();
- }
- service.shutdown();
- logger.info("XMPP server destroyed");
- }
-
- public int getServerPort() {
- return s2sPort;
- }
-}
diff --git a/src/main/java/com/juick/server/api/ApiSocialLogin.java b/src/main/java/com/juick/server/api/ApiSocialLogin.java
index 72cda0af..be306fe9 100644
--- a/src/main/java/com/juick/server/api/ApiSocialLogin.java
+++ b/src/main/java/com/juick/server/api/ApiSocialLogin.java
@@ -82,6 +82,7 @@ public class ApiSocialLogin {
@Inject
private ObjectMapper jsonMapper;
private ServiceBuilder facebookBuilder, twitterBuilder, vkBuilder;
+ private OAuth20Service facebookAuthService, vkAuthService;
@Value("${twitter_consumer_key:appid}")
private String twitterConsumerKey;
@@ -117,6 +118,16 @@ public class ApiSocialLogin {
verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Collections.singletonList(googleClientId))
.build();
+ facebookAuthService = facebookBuilder
+ .apiSecret(FACEBOOK_SECRET)
+ .callback(FACEBOOK_REDIRECT)
+ .scope("email")
+ .build(FacebookApi.instance());
+ vkAuthService = vkBuilder
+ .apiSecret(VK_SECRET)
+ .scope("friends,wall,offline")
+ .callback(VK_REDIRECT)
+ .build(VkontakteApi.instance());
}
@GetMapping("/api/_fblogin")
@@ -125,13 +136,7 @@ public class ApiSocialLogin {
if (StringUtils.isBlank(code)) {
String fbstate = UUID.randomUUID().toString();
crosspostService.addFacebookState(fbstate, state);
- OAuth20Service facebookAuthService = facebookBuilder
- .apiSecret(FACEBOOK_SECRET)
- .callback(FACEBOOK_REDIRECT)
- .scope("email")
- .state(fbstate)
- .build(FacebookApi.instance());
- return "redirect:" + facebookAuthService.getAuthorizationUrl();
+ return "redirect:" + facebookAuthService.getAuthorizationUrl(fbstate);
}
String redirectUrl = crosspostService.verifyFacebookState(state);
@@ -140,53 +145,43 @@ public class ApiSocialLogin {
logger.error("state is missing");
throw new HttpBadRequestException();
}
- OAuth20Service facebookService = facebookBuilder
- .apiKey(FACEBOOK_APPID)
- .apiSecret(FACEBOOK_SECRET)
- .callback(FACEBOOK_REDIRECT)
- .scope("email")
- .state(state)
- .build(FacebookApi.instance());
- OAuth2AccessToken token = facebookService.getAccessToken(code);
- final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email");
- facebookService.signRequest(token, meRequest);
- String graph = facebookService.execute(meRequest).getBody();
+ OAuth2AccessToken token = facebookAuthService.getAccessToken(code);
+ final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v3.2/me?fields=id,name,email");
+ facebookAuthService.signRequest(token, meRequest);
+ String graph = facebookAuthService.execute(meRequest).getBody();
if (StringUtils.isBlank(graph)) {
logger.error("FACEBOOK GRAPH ERROR");
throw new HttpBadRequestException();
}
User fb = jsonMapper.readValue(graph, User.class);
long fbID = NumberUtils.toLong(fb.getId(), 0);
- if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) {
- logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink());
+ if (fbID == 0 || StringUtils.isBlank(fb.getName())) {
+ logger.error("Missing required fields, id: {}, name: {}", fbID, fb.getName());
throw new HttpBadRequestException();
}
int uid = crosspostService.getUIDbyFBID(fbID);
if (uid > 0) {
- if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) {
+ if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName())) {
logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken());
throw new HttpBadRequestException();
}
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl);
uriComponentsBuilder.queryParam("hash", userService.getHashByUID(uid));
return "redirect:" + uriComponentsBuilder.build().toUriString();
- } else if (fb.getVerified()) {
- if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) {
+ } else {
+ if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName())) {
if (StringUtils.isNotEmpty(fb.getEmail())) {
- logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink());
+ logger.info("found {} for facebook user {}", fb.getEmail());
Integer userId = crosspostService.getUIDbyFBID(fbID);
if (!emailService.getEmails(userId, false).contains(fb.getEmail())) {
emailService.addEmail(userId, fb.getEmail());
}
}
- logger.info("email not found for facebook user {}", fb.getLink());
+ logger.info("email not found for facebook user {}", fb.getName());
throw new HttpBadRequestException();
}
return "redirect:/signup?type=fb&hash=" + state;
- } else {
- logger.error("Facebook account is not verified, id: {}", fbID);
- throw new HttpBadRequestException();
}
}/*
@GetMapping("/_twitter")
@@ -244,13 +239,7 @@ public class ApiSocialLogin {
if (StringUtils.isBlank(code)) {
String vkstate = UUID.randomUUID().toString();
crosspostService.addVKState(vkstate, state);
- OAuth20Service vkAuthService = vkBuilder
- .apiSecret(VK_SECRET)
- .scope("friends,wall,offline")
- .state(vkstate)
- .callback(VK_REDIRECT)
- .build(VkontakteApi.instance());
- return "redirect:" + vkAuthService.getAuthorizationUrl();
+ return "redirect:" + vkAuthService.getAuthorizationUrl(vkstate);
}
String redirectUrl = crosspostService.verifyVKState(state);
@@ -258,16 +247,11 @@ public class ApiSocialLogin {
logger.error("state is missing");
throw new HttpBadRequestException();
}
-
- OAuth20Service vkService = vkBuilder
- .apiKey(VK_APPID)
- .apiSecret(VK_SECRET)
- .build(VkontakteApi.instance());
- OAuth2AccessToken token = vkService.getAccessToken(code);
+ OAuth2AccessToken token = vkAuthService.getAccessToken(code);
OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73");
- vkService.signRequest(token, meRequest);
- String graph = vkService.execute(meRequest).getBody();
+ vkAuthService.signRequest(token, meRequest);
+ String graph = vkAuthService.execute(meRequest).getBody();
com.juick.model.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0);
String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName();
diff --git a/src/main/java/com/juick/server/api/Index.java b/src/main/java/com/juick/server/api/Index.java
index 56f01370..4573641b 100644
--- a/src/main/java/com/juick/server/api/Index.java
+++ b/src/main/java/com/juick/server/api/Index.java
@@ -17,10 +17,7 @@
package com.juick.server.api;
-import com.juick.Status;
-import com.juick.server.WebsocketManager;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -28,7 +25,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import springfox.documentation.annotations.ApiIgnore;
-import javax.inject.Inject;
import java.net.URI;
/**
@@ -36,19 +32,11 @@ import java.net.URI;
*/
@RestController
public class Index {
- @Inject
- private WebsocketManager wsHandler;
@ApiIgnore
- @RequestMapping(value = { "/api/", "/ws/" }, method = RequestMethod.GET, headers = "Connection!=Upgrade")
+ @RequestMapping(value = { "/api/", "/ws/" }, method = RequestMethod.GET)
public ResponseEntity<Void> description() {
URI redirectUri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/swagger-ui.html").build().toUri();
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).location(redirectUri).build();
}
- @ApiIgnore
- @RequestMapping(value = "/api/status", method = RequestMethod.GET,
- produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = "Connection!=Upgrade")
- public Status status() {
- return Status.getStatus(String.valueOf(wsHandler.getClients().size()));
- }
}
diff --git a/src/main/java/com/juick/server/api/Messages.java b/src/main/java/com/juick/server/api/Messages.java
index e105890f..5c791df4 100644
--- a/src/main/java/com/juick/server/api/Messages.java
+++ b/src/main/java/com/juick/server/api/Messages.java
@@ -179,6 +179,9 @@ public class Messages {
}
msg.getUser().setAvatar(webApp.getAvatarUrl(msg.getUser()));
msg.setRecommendations(new HashSet<>(messagesService.getMessageRecommendations(msg.getMid())));
+ msg.getRecommendations().forEach(r -> {
+ r.setAvatar(webApp.getAvatarUrl(r));
+ });
List<com.juick.Message> replies = messagesService.getReplies(visitor, mid);
replies.forEach(m -> m.getUser().setAvatar(webApp.getAvatarUrl(m.getUser())));
if (!visitor.isAnonymous()) {
diff --git a/src/main/java/com/juick/server/api/Notifications.java b/src/main/java/com/juick/server/api/Notifications.java
index 000a9f3b..ea1d5c54 100644
--- a/src/main/java/com/juick/server/api/Notifications.java
+++ b/src/main/java/com/juick/server/api/Notifications.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2017, Juick
+ * Copyright (C) 2008-2019, Juick
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -24,14 +24,19 @@ import com.juick.User;
import com.juick.model.AnonymousUser;
import com.juick.server.util.HttpBadRequestException;
import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.UserUtils;
import com.juick.service.MessagesService;
import com.juick.service.PushQueriesService;
import com.juick.service.SubscriptionService;
-import com.juick.server.util.UserUtils;
+import com.juick.service.TelegramService;
import com.juick.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.inject.Inject;
@@ -55,6 +60,8 @@ public class Notifications {
private SubscriptionService subscriptionService;
@Inject
private UserService userService;
+ @Inject
+ private TelegramService telegramService;
private User collectTokens(Integer uid) {
@@ -63,6 +70,14 @@ public class Notifications {
pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null)));
pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null)));
pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null)));
+ List<ExternalToken> xmppJids = userService.getJIDsbyUID(uid).stream()
+ .map(jid -> new ExternalToken(null, "xmpp", jid, null))
+ .collect(Collectors.toList());
+ user.getTokens().addAll(xmppJids);
+ List<ExternalToken> tgIds = telegramService.getTelegramIdentifiers(Collections.singletonList(user)).stream()
+ .map(tgId -> new ExternalToken(null, "durov", String.valueOf(tgId), null))
+ .collect(Collectors.toList());
+ user.getTokens().addAll(tgIds);
return user;
}
diff --git a/src/main/java/com/juick/server/api/PM.java b/src/main/java/com/juick/server/api/PM.java
index a80ad0dc..e61fef6e 100644
--- a/src/main/java/com/juick/server/api/PM.java
+++ b/src/main/java/com/juick/server/api/PM.java
@@ -99,6 +99,7 @@ public class PM {
jmsg.setUser(visitor);
jmsg.setText(body);
jmsg.setTo(userTo);
+ jmsg.getUser().setAvatar(webApp.getAvatarUrl(jmsg.getUser()));
applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg, Collections.singletonList(jmsg.getTo())));
return jmsg;
diff --git a/src/main/java/com/juick/server/api/Users.java b/src/main/java/com/juick/server/api/Users.java
index 6f9ab290..33b3704b 100644
--- a/src/main/java/com/juick/server/api/Users.java
+++ b/src/main/java/com/juick/server/api/Users.java
@@ -18,21 +18,24 @@
package com.juick.server.api;
import com.juick.User;
+import com.juick.model.AnonymousUser;
import com.juick.model.ApplicationStatus;
-import com.juick.model.UserInfo;
-import com.juick.server.util.HttpForbiddenException;
import com.juick.server.util.HttpNotFoundException;
-import com.juick.server.www.WebApp;
-import com.juick.service.CrosspostService;
-import com.juick.service.EmailService;
-import com.juick.service.MessagesService;
-import com.juick.service.UserService;
+import com.juick.server.util.HttpUtils;
import com.juick.server.util.UserUtils;
import com.juick.server.util.WebUtils;
+import com.juick.server.www.WebApp;
+import com.juick.service.*;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
import javax.inject.Inject;
+import java.io.IOException;
+import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -52,6 +55,10 @@ public class Users {
private EmailService emailService;
@Inject
private WebApp webApp;
+ @Inject
+ private ImagesService imagesService;
+ @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
+ private String tmpDir;
@RequestMapping(value = "/api/auth", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String getAuthToken() {
@@ -94,16 +101,21 @@ public class Users {
me.setRead(userService.getUserFriends(visitor.getUid()));
me.setReaders(userService.getUserReaders(visitor.getUid()));
me.setAvatar(webApp.getAvatarUrl(visitor));
- return me;
+ return (SecureUser)userService.getUserInfo(me);
+ }
+ @PostMapping("/api/me/upload")
+ public void updateInfo(@RequestParam MultipartFile avatar) throws IOException {
+ User visitor = UserUtils.getCurrentUser();
+ String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost();
+ if (StringUtils.isNotEmpty(avatarTmpPath)) {
+ imagesService.saveAvatar(avatarTmpPath, visitor.getUid());
+ }
}
@RequestMapping(value = "/api/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public List<User> doGetUserRead(
@RequestParam String uname) {
User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous()) {
- throw new HttpForbiddenException();
- }
int uid = 0;
if (uname == null) {
uid = visitor.getUid();
@@ -128,9 +140,6 @@ public class Users {
public List<User> doGetUserReaders(
@RequestParam String uname) {
User visitor = UserUtils.getCurrentUser();
- if (visitor.isAnonymous()) {
- throw new HttpForbiddenException();
- }
int uid = 0;
if (uname == null) {
uid = visitor.getUid();
@@ -152,22 +161,36 @@ public class Users {
}
@GetMapping("/api/info/{uname}")
- public UserInfo getUserInfo(@PathVariable String uname) {
+ public User getUserInfo(@PathVariable String uname) {
User user = userService.getUserByName(uname);
if (!user.isBanned()) {
+ user.setRead(doGetUserRead(uname));
+ user.setReaders(doGetUserReaders(uname));
user.setAvatar(webApp.getAvatarUrl(user));
return userService.getUserInfo(user);
}
throw new HttpNotFoundException();
}
+ @Deprecated
+ @GetMapping(value = "/api/avatar", produces = MediaType.IMAGE_PNG_VALUE)
+ public byte[] getAvatarUrl(
+ @RequestParam(required = false) String uname,
+ @RequestParam(required = false) String jid)
+ throws IOException {
+ User user = AnonymousUser.INSTANCE;
+ if (StringUtils.isNotEmpty(uname)) {
+ user = userService.getUserByName(uname);
+ }
+ if (user.isAnonymous() && StringUtils.isNotEmpty(jid)) {
+ user = userService.getUserByJID(jid);
+ }
+ return IOUtils.toByteArray(URI.create(webApp.getAvatarUrl(user)));
+ }
class SecureUser extends User {
public String getHash() {
return getAuthHash();
}
- public UserInfo getUserInfo() {
- return userService.getUserInfo(this);
- }
public List<String> getJIDs() {
return userService.getAllJIDs(this);
}
diff --git a/src/main/java/com/juick/server/api/activity/Profile.java b/src/main/java/com/juick/server/api/activity/Profile.java
index 305b7c4a..4e375a54 100644
--- a/src/main/java/com/juick/server/api/activity/Profile.java
+++ b/src/main/java/com/juick/server/api/activity/Profile.java
@@ -3,6 +3,7 @@ package com.juick.server.api.activity;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.juick.Message;
import com.juick.User;
+import com.juick.formatters.PlainTextFormatter;
import com.juick.model.CommandResult;
import com.juick.server.ActivityPubManager;
import com.juick.server.CommandsManager;
@@ -29,6 +30,8 @@ import com.juick.server.www.WebApp;
import com.juick.service.MessagesService;
import com.juick.service.UserService;
import com.juick.service.activities.*;
+import com.overzealous.remark.Remark;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,12 +47,15 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.inject.Inject;
+import java.io.InputStream;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -81,6 +87,8 @@ public class Profile {
private ObjectMapper jsonMapper;
@Inject
private WebApp webApp;
+ @Inject
+ private Remark remarkConverter;
@GetMapping(value = "/u/{userName}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
public Person getUser(@PathVariable String userName) {
@@ -252,32 +260,17 @@ public class Profile {
}
@PostMapping(value = "/api/inbox", consumes = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
- public ResponseEntity<Void> processInbox(@RequestBody Activity activity,
- @RequestHeader(name = "Host") String host,
- @RequestHeader(name = "Date") String date,
- @RequestHeader(name = "Digest", required = false) String digest,
- @RequestHeader(name = "Content-Type") String contentType,
- @RequestHeader(name = "User-Agent", required = false) String userAgent,
- @RequestHeader(name = "Accept-Encoding", required = false) String acceptEncoding,
- @RequestHeader(name = "Signature", required = false) String signature) throws Exception {
- UriComponents componentsBuilder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- Map<String, String> headers = new HashMap<>();
- headers.put("host", host.split(":", 2)[0]);
- headers.put("date", date);
- headers.put("digest", digest);
- headers.put("content-type", contentType);
- headers.put("user-agent", userAgent);
- headers.put("accept-encoding", acceptEncoding);
- boolean valid = signatureManager.verifySignature(signature, URI.create(activity.getActor()), "POST",
- componentsBuilder.getPath(), headers);
- if (valid) {
+ public ResponseEntity<CommandResult> processInbox(InputStream inboxData) throws Exception {
+ String inbox = IOUtils.toString(inboxData, StandardCharsets.UTF_8);
+ logger.info("Inbox: {}", inbox);
+ Activity activity = jsonMapper.readValue(inbox, Activity.class);
+ User visitor = UserUtils.getCurrentUser();
+ if ((StringUtils.isNotEmpty(visitor.getUri().toString()) && visitor.getUri().equals(URI.create(activity.getActor()))) || !visitor.isAnonymous()) {
if (activity instanceof Follow) {
Follow followRequest = (Follow) activity;
- String actor = followRequest.getActor();
- Person follower = (Person) signatureManager.getContext(URI.create(actor)).orElseThrow(HttpBadRequestException::new);
applicationEventPublisher.publishEvent(
new FollowEvent(this, followRequest));
- return new ResponseEntity<>(HttpStatus.ACCEPTED);
+ return new ResponseEntity<>(CommandResult.fromString("Follow request accepted"), HttpStatus.ACCEPTED);
}
if (activity instanceof Undo) {
@@ -286,17 +279,10 @@ public class Profile {
String objectObject = (String) object.get("object");
if (objectType.equals("Follow")) {
applicationEventPublisher.publishEvent(new UndoFollowEvent(this, activity.getActor(), objectObject));
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Undo follow request accepted"), HttpStatus.OK);
} else if (objectType.equals("Like") || objectType.equals("Announce")) {
applicationEventPublisher.publishEvent(new UndoAnnounceEvent(this, activity.getActor(), objectObject));
- return new ResponseEntity<>(HttpStatus.OK);
- }
- }
- if (activity instanceof Delete) {
- if (activity.getObject() instanceof String) {
- // Delete user
- applicationEventPublisher.publishEvent(new DeleteUserEvent(this, (String)activity.getObject()));
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Undo like/announce request accepted"), HttpStatus.OK);
}
}
if (activity instanceof Create) {
@@ -305,7 +291,7 @@ public class Profile {
if (note.get("type").equals("Note")) {
URI noteId = URI.create((String) note.get("id"));
if (messagesService.replyExists(noteId)) {
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Reply already exists"), HttpStatus.OK);
} else {
String inReplyTo = (String) note.get("inReplyTo");
if (StringUtils.isNotBlank(inReplyTo)) {
@@ -313,36 +299,50 @@ public class Profile {
String postId = activityPubManager.postId(inReplyTo);
User user = new User();
user.setUri(URI.create(activity.getActor()));
- String attachment = StringUtils.EMPTY;
- if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) {
- Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0);
- attachment = (String) attachmentObj.get("url");
- }
- CommandResult result = commandsManager.processCommand(user, String.format("#%s %s", postId, note.get("content")), URI.create(attachment));
+ String markdown = remarkConverter.convertFragment((String) note.get("content"));
+ String commandBody = note.get("attachment") == null ? markdown :
+ ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
+ Map<String, String> attachment = (Map<String, String>) attachmentObj;
+ String attachmentUrl = attachment.get("url");
+ String attachmentName = attachment.get("name");
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce((source, url) -> String.format("%s\n%s", source, url))
+ .orElse(markdown);
+
+ CommandResult result = commandsManager.processCommand(
+ user, String.format("#%s %s", postId, commandBody),
+ URI.create(StringUtils.EMPTY));
logger.info(jsonMapper.writeValueAsString(result));
if (result.getNewMessage().isPresent()) {
messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(result, HttpStatus.OK);
} else {
- return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
} else {
Message reply = messagesService.getReplyByUri(inReplyTo);
if (reply != null) {
User user = new User();
user.setUri(URI.create(activity.getActor()));
- String attachment = StringUtils.EMPTY;
- if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) {
- Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0);
- attachment = (String) attachmentObj.get("url");
- }
- CommandResult result = commandsManager.processCommand(user, String.format("#%d/%d %s", reply.getMid(), reply.getRid(), note.get("content")), URI.create(attachment));
+ String markdown = remarkConverter.convertFragment((String)note.get("content"));
+ String commandBody = note.get("attachment") == null ? markdown :
+ ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
+ Map<String, String> attachment = (Map<String, String>) attachmentObj;
+ String attachmentUrl = attachment.get("url");
+ String attachmentName = attachment.get("name");
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce((source, url) -> String.format("%s\n%s", source, url))
+ .orElse(markdown);
+ CommandResult result = commandsManager.processCommand(
+ user,
+ String.format("#%d/%d %s", reply.getMid(), reply.getRid(), commandBody),
+ URI.create(StringUtils.EMPTY));
logger.info(jsonMapper.writeValueAsString(result));
if (result.getNewMessage().isPresent()) {
messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(result, HttpStatus.OK);
} else {
- return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
}
}
@@ -357,17 +357,28 @@ public class Profile {
URI actor = URI.create(activity.getActor());
URI reply = URI.create((String)tombstone.get("id"));
messagesService.deleteReply(actor, reply);
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.OK);
}
}
if (activity instanceof Like || activity instanceof Announce) {
- applicationEventPublisher.publishEvent(new AnnounceEvent(this, activity.getActor(), (String)activity.getObject()));
- return new ResponseEntity<>(HttpStatus.OK);
+ String messageUri = activity.getObject() instanceof String ? (String) activity.getObject()
+ : activity.getObject() instanceof Context ? ((Context) activity.getObject()).getId()
+ : (String) ((Map)activity.getObject()).get("id");
+ applicationEventPublisher.publishEvent(new AnnounceEvent(this, activity.getActor(), messageUri));
+ return new ResponseEntity<>(CommandResult.fromString("Like/announce request accepted"), HttpStatus.OK);
}
logger.warn("Unknown activity: {}", jsonMapper.writeValueAsString(activity));
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ return new ResponseEntity<>(CommandResult.fromString("Unknown activity"), HttpStatus.NOT_IMPLEMENTED);
+ }
+ if (activity instanceof Delete) {
+ if (activity.getObject() instanceof String) {
+ // Delete gone user
+ if (activity.getActor().equals(activity.getObject())) {
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED);
+ }
+ }
}
- return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+ return new ResponseEntity<>(CommandResult.fromString("Can not authenticate"), HttpStatus.UNAUTHORIZED);
}
@PostMapping(value = "/u/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User fetchUser(@RequestParam URI uri) {
diff --git a/src/main/java/com/juick/server/api/activity/helpers/ActivityIdDeserializer.java b/src/main/java/com/juick/server/api/activity/helpers/ActivityIdDeserializer.java
new file mode 100644
index 00000000..ba8cfb87
--- /dev/null
+++ b/src/main/java/com/juick/server/api/activity/helpers/ActivityIdDeserializer.java
@@ -0,0 +1,22 @@
+package com.juick.server.api.activity.helpers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+
+public class ActivityIdDeserializer extends JsonDeserializer<String> {
+ @Override
+ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+ JsonToken jsonToken = p.getCurrentToken();
+ if (jsonToken == JsonToken.START_OBJECT) {
+ JsonNode node = p.getCodec().readTree(p);
+ return node.get("id").textValue();
+ }
+ return p.getValueAsString();
+ }
+}
diff --git a/src/main/java/com/juick/server/api/activity/helpers/LinkValueDeserializer.java b/src/main/java/com/juick/server/api/activity/helpers/LinkValueDeserializer.java
new file mode 100644
index 00000000..a635ea95
--- /dev/null
+++ b/src/main/java/com/juick/server/api/activity/helpers/LinkValueDeserializer.java
@@ -0,0 +1,21 @@
+package com.juick.server.api.activity.helpers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+
+public class LinkValueDeserializer extends JsonDeserializer<String> {
+ @Override
+ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ JsonToken jsonToken = p.getCurrentToken();
+ if (jsonToken == JsonToken.VALUE_STRING) {
+ return p.getValueAsString();
+ }
+ JsonNode node = p.getCodec().readTree(p);
+ return node.get("href").textValue();
+ }
+}
diff --git a/src/main/java/com/juick/server/api/activity/model/Activity.java b/src/main/java/com/juick/server/api/activity/model/Activity.java
index ec126b88..2af14479 100644
--- a/src/main/java/com/juick/server/api/activity/model/Activity.java
+++ b/src/main/java/com/juick/server/api/activity/model/Activity.java
@@ -1,7 +1,11 @@
package com.juick.server.api.activity.model;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.juick.server.api.activity.helpers.ActivityIdDeserializer;
+
public abstract class Activity extends Context {
+ @JsonDeserialize(using = ActivityIdDeserializer.class)
private String actor;
private Object object;
diff --git a/src/main/java/com/juick/server/api/activity/model/Context.java b/src/main/java/com/juick/server/api/activity/model/Context.java
index 515ee3da..2ba4606e 100644
--- a/src/main/java/com/juick/server/api/activity/model/Context.java
+++ b/src/main/java/com/juick/server/api/activity/model/Context.java
@@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.juick.server.api.activity.helpers.LinkValueDeserializer;
import com.juick.server.api.activity.model.activities.*;
import com.juick.server.api.activity.model.objects.*;
@@ -46,6 +48,7 @@ public abstract class Context {
private Instant published;
+ @JsonDeserialize(using = LinkValueDeserializer.class)
private String url;
private List<String> to;
diff --git a/src/main/java/com/juick/server/api/rss/MessagesView.java b/src/main/java/com/juick/server/api/rss/MessagesView.java
index 05bc0bd6..2c05c8cb 100644
--- a/src/main/java/com/juick/server/api/rss/MessagesView.java
+++ b/src/main/java/com/juick/server/api/rss/MessagesView.java
@@ -126,7 +126,7 @@ public class MessagesView extends AbstractRssFeedView {
description.setType("text/html");
description.setValue(messageDescription);
item.setDescription(description);
- item.setPubDate(Date.from(msg.getTimestamp()));
+ item.setPubDate(Date.from(msg.getCreated()));
item.setComments(messageUrl);
msg.getTags().stream().map(t -> {
Category category = new Category();
diff --git a/src/main/java/com/juick/server/api/webfinger/Resource.java b/src/main/java/com/juick/server/api/webfinger/Resource.java
index 71a0ca31..4e0447f7 100644
--- a/src/main/java/com/juick/server/api/webfinger/Resource.java
+++ b/src/main/java/com/juick/server/api/webfinger/Resource.java
@@ -26,7 +26,7 @@ public class Resource {
@Value("${ap_base_uri:http://localhost:8080/}")
private String baseUri;
- @GetMapping("/.well-known/webfinger")
+ @GetMapping(value = "/.well-known/webfinger", produces = "application/jrd+json;charset=utf-8")
public Account getWebResource(@RequestParam String resource) {
if (resource.startsWith("acct:")) {
Jid account = Jid.of(resource.substring(5));
diff --git a/src/main/java/com/juick/server/api/xnodeinfo2/Info.java b/src/main/java/com/juick/server/api/xnodeinfo2/Info.java
index 36e36bdd..493bff6a 100644
--- a/src/main/java/com/juick/server/api/xnodeinfo2/Info.java
+++ b/src/main/java/com/juick/server/api/xnodeinfo2/Info.java
@@ -1,15 +1,24 @@
package com.juick.server.api.xnodeinfo2;
+import com.cliqset.xrd.Link;
+import com.cliqset.xrd.XRD;
+import com.fasterxml.jackson.annotation.JsonView;
import com.juick.server.api.xnodeinfo2.model.*;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.util.UriComponentsBuilder;
import javax.inject.Inject;
+import java.net.URI;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
@RestController
public class Info {
@@ -18,9 +27,9 @@ public class Info {
@Inject
private JdbcTemplate jdbcTemplate;
- @GetMapping("/.well-known/x-nodeinfo2")
- public NodeInfo showNodeInfo() {
+ private NodeInfo getCurrentNodeInfo(String version) {
NodeInfo nodeInfo = new NodeInfo();
+ nodeInfo.setVersion(version);
Server server = new Server();
server.setBaseUrl(baseUri);
server.setName("Juick");
@@ -28,6 +37,9 @@ public class Info {
server.setVersion("2.x");
nodeInfo.setServer(server);
nodeInfo.setProtocols(Arrays.asList("xmpp", "activitypub", "smtp"));
+ Map<String, String> metadata = new HashMap<>();
+ metadata.put("email", "support@juick.com");
+ nodeInfo.setMetadata(metadata);
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setInbound(Arrays.asList("jabber", "mastodon", "email", "telegram"));
serviceInfo.setOutbound(Arrays.asList("jabber", "mastodon", "telegram", "twitter", "email", "rss"));
@@ -47,4 +59,28 @@ public class Info {
nodeInfo.setUsage(usage);
return nodeInfo;
}
+
+ @GetMapping(value = "/.well-known/x-nodeinfo2", produces = MediaType.APPLICATION_JSON_VALUE)
+ @JsonView(NodeInfo.XNodeInfoView.class)
+ public NodeInfo showXNodeInfo() {
+ return getCurrentNodeInfo("1.0");
+ }
+
+ @GetMapping(value = "/.well-known/nodeinfo", produces = MediaType.APPLICATION_JSON_VALUE)
+ public XRD getNodeInfoLinks() {
+ Link nodeinfo = new Link();
+ nodeinfo.setRel(URI.create("http://nodeinfo.diaspora.software/ns/schema/2.0"));
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ uriComponentsBuilder.replacePath("/api/nodeinfo/2.0");
+ nodeinfo.setHref(uriComponentsBuilder.build().toUri());
+ XRD xrd = new XRD();
+ xrd.setLinks(Collections.singletonList(nodeinfo));
+ return xrd;
+ }
+
+ @GetMapping(value = "/api/nodeinfo/2.0", produces = MediaType.APPLICATION_JSON_VALUE)
+ @JsonView(NodeInfo.NodeInfoView.class)
+ public NodeInfo showNodeInfo() {
+ return getCurrentNodeInfo("2.0");
+ }
}
diff --git a/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java b/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java
index 06fe354f..1ebe39a8 100644
--- a/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java
+++ b/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java
@@ -1,27 +1,45 @@
package com.juick.server.api.xnodeinfo2.model;
+import com.fasterxml.jackson.annotation.JsonView;
+
import java.util.List;
+import java.util.Map;
public class NodeInfo {
+ private String version;
+
private Server server;
private List<String> protocols;
private ServiceInfo services;
+ private Map<String, String> metadata;
+
+ @JsonView({XNodeInfoView.class, NodeInfoView.class})
public String getVersion() {
- return "1.0";
+ return version;
}
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ @JsonView(XNodeInfoView.class)
public Server getServer() {
return server;
}
+ @JsonView(NodeInfoView.class)
+ public Server getSoftware() {
+ return server;
+ }
public void setServer(Server server) {
this.server = server;
}
+ @JsonView({XNodeInfoView.class, NodeInfoView.class})
public List<String> getProtocols() {
return protocols;
}
@@ -30,6 +48,7 @@ public class NodeInfo {
this.protocols = protocols;
}
+ @JsonView({XNodeInfoView.class, NodeInfoView.class})
public ServiceInfo getServices() {
return services;
}
@@ -38,12 +57,14 @@ public class NodeInfo {
this.services = services;
}
+ @JsonView({XNodeInfoView.class, NodeInfoView.class})
public boolean getOpenRegistrations() {
return true;
}
private Usage usage;
+ @JsonView({XNodeInfoView.class, NodeInfoView.class})
public Usage getUsage() {
return usage;
}
@@ -51,4 +72,16 @@ public class NodeInfo {
public void setUsage(Usage usage) {
this.usage = usage;
}
+
+ @JsonView(NodeInfoView.class)
+ public Map<String, String> getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map<String, String> metadata) {
+ this.metadata = metadata;
+ }
+
+ public interface NodeInfoView {}
+ public interface XNodeInfoView {}
}
diff --git a/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java b/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java
index a772d268..08c1a696 100644
--- a/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java
+++ b/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java
@@ -1,11 +1,14 @@
package com.juick.server.api.xnodeinfo2.model;
+import com.fasterxml.jackson.annotation.JsonView;
+
public class Server {
private String baseUrl;
private String name;
private String software;
private String version;
+ @JsonView(NodeInfo.XNodeInfoView.class)
public String getBaseUrl() {
return baseUrl;
}
@@ -14,6 +17,7 @@ public class Server {
this.baseUrl = baseUrl;
}
+ @JsonView({NodeInfo.NodeInfoView.class, NodeInfo.XNodeInfoView.class})
public String getName() {
return name;
}
@@ -22,6 +26,7 @@ public class Server {
this.name = name;
}
+ @JsonView(NodeInfo.XNodeInfoView.class)
public String getSoftware() {
return software;
}
@@ -30,6 +35,7 @@ public class Server {
this.software = software;
}
+ @JsonView({NodeInfo.NodeInfoView.class, NodeInfo.XNodeInfoView.class})
public String getVersion() {
return version;
}
diff --git a/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java b/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java
index 5b6d2baa..0114488f 100644
--- a/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java
+++ b/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java
@@ -1,7 +1,10 @@
package com.juick.server.api.xnodeinfo2.model;
+import com.fasterxml.jackson.annotation.JsonView;
+
import java.util.List;
+@JsonView({NodeInfo.NodeInfoView.class, NodeInfo.XNodeInfoView.class})
public class ServiceInfo {
private List<String> inbound;
private List<String> outbound;
diff --git a/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java b/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java
index e04ea48b..f270296f 100644
--- a/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java
+++ b/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java
@@ -1,5 +1,8 @@
package com.juick.server.api.xnodeinfo2.model;
+import com.fasterxml.jackson.annotation.JsonView;
+
+@JsonView({NodeInfo.NodeInfoView.class, NodeInfo.XNodeInfoView.class})
public class Usage {
private UserStats users;
private int localPosts;
diff --git a/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java b/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java
index 515661e3..cca31be6 100644
--- a/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java
+++ b/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java
@@ -1,5 +1,8 @@
package com.juick.server.api.xnodeinfo2.model;
+import com.fasterxml.jackson.annotation.JsonView;
+
+@JsonView({NodeInfo.NodeInfoView.class, NodeInfo.XNodeInfoView.class})
public class UserStats {
private int total;
private int activeHalfyear;
diff --git a/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java b/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java
index 43b638fe..d7d49355 100644
--- a/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java
+++ b/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java
@@ -1,11 +1,13 @@
package com.juick.server.configuration;
+import com.juick.server.api.activity.model.Activity;
+import com.juick.server.helpers.HeaderRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.inject.Inject;
+import java.util.Collections;
@Configuration
public class ActivityPubClientConfig {
@@ -13,8 +15,10 @@ public class ActivityPubClientConfig {
ActivityPubClientErrorHandler activityPubClientErrorHandler;
@Bean
public RestTemplate apClient() {
- RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
+ RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(activityPubClientErrorHandler);
+ restTemplate.setInterceptors(Collections.singletonList(
+ new HeaderRequestInterceptor("Accept", Activity.ACTIVITY_MEDIA_TYPE)));
return restTemplate;
}
} \ No newline at end of file
diff --git a/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java b/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
index 5a5d2c7b..68b3d35f 100644
--- a/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
+++ b/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
@@ -17,25 +17,15 @@
package com.juick.server.configuration;
-import com.juick.server.WebsocketManager;
import com.juick.server.api.rss.MessagesView;
import com.juick.server.api.rss.RepliesView;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.Ordered;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.feed.AbstractRssFeedView;
-import org.springframework.web.socket.config.annotation.EnableWebSocket;
-import org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry;
-import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
-import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
-import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
/**
* Created by aalexeev on 11/12/16.
@@ -43,24 +33,7 @@ import javax.inject.Inject;
@Configuration
@EnableAsync(proxyTargetClass = true)
@EnableScheduling
-@EnableWebSocket
-public class ApiAppConfiguration implements WebMvcConfigurer, WebSocketConfigurer {
- @Inject
- private WebsocketManager websocketManager;
-
- @Override
- public void registerWebSocketHandlers(@Nonnull WebSocketHandlerRegistry registry) {
- ((ServletWebSocketHandlerRegistry) registry).setOrder(Ordered.HIGHEST_PRECEDENCE);
- registry.addHandler(websocketManager, "/ws/**").setAllowedOrigins("*");
- }
-
- @Bean
- public ServletServerContainerFactoryBean createWebSocketContainer() {
- ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
- container.setMaxTextMessageBufferSize(8192);
- container.setMaxBinaryMessageBufferSize(8192);
- return container;
- }
+public class ApiAppConfiguration implements WebMvcConfigurer {
@Bean
public BeanNameViewResolver beanNameViewResolver() {
return new BeanNameViewResolver();
diff --git a/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java b/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java
index 6a2a8142..c8b88cd1 100644
--- a/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java
+++ b/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java
@@ -17,6 +17,10 @@
package com.juick.server.configuration;
+import com.juick.server.KeystoreManager;
+import com.overzealous.remark.Options;
+import com.overzealous.remark.Remark;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
@@ -36,6 +40,10 @@ import java.util.concurrent.Executors;
@Configuration
public class BaseWebConfiguration implements WebMvcConfigurer, SchedulingConfigurer {
+ @Value("${keystore:juick.p12}")
+ private String keystore;
+ @Value("${keystore_password:secret}")
+ private String keystorePassword;
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
@@ -61,4 +69,14 @@ public class BaseWebConfiguration implements WebMvcConfigurer, SchedulingConfigu
public ExecutorService executorService() {
return Executors.newCachedThreadPool();
}
+ @Bean
+ public KeystoreManager keystoreManager() {
+ return new KeystoreManager(keystore, keystorePassword);
+ }
+ @Bean
+ public Remark remarkConverter() {
+ Options options = new Options();
+ options.inlineLinks = true;
+ return new Remark(options);
+ }
}
diff --git a/src/main/java/com/juick/server/configuration/SecurityConfig.java b/src/main/java/com/juick/server/configuration/SecurityConfig.java
index f53cc531..df0da16e 100644
--- a/src/main/java/com/juick/server/configuration/SecurityConfig.java
+++ b/src/main/java/com/juick/server/configuration/SecurityConfig.java
@@ -17,7 +17,9 @@
package com.juick.server.configuration;
+import com.juick.server.SignatureManager;
import com.juick.service.UserService;
+import com.juick.service.security.HTTPSignatureAuthenticationFilter;
import com.juick.service.security.HashParamAuthenticationFilter;
import com.juick.service.security.JuickUserDetailsService;
import com.juick.service.security.deprecated.RequestParamHashRememberMeServices;
@@ -69,6 +71,20 @@ public class SecurityConfig {
public UserDetailsService userDetailsService() {
return new JuickUserDetailsService(userService);
}
+ @Bean
+ static CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+
+ configuration.setAllowedOrigins(Collections.singletonList("*"));
+ configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "OPTIONS", "DELETE"));
+ configuration.setAllowedHeaders(Collections.singletonList("*"));
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/api/**", configuration);
+ source.registerCorsConfiguration("/u/**", configuration);
+ source.registerCorsConfiguration("/n/**", configuration);
+ return source;
+ }
@Configuration
@Order(1)
@@ -79,6 +95,8 @@ public class SecurityConfig {
private String webDomain;
@Resource
private UserService userService;
+ @Resource
+ private SignatureManager signatureManager;
ApiConfig() {
super(true);
}
@@ -95,10 +113,14 @@ public class SecurityConfig {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class)
+ .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), BasicAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
- .antMatchers("/api/", "/api/messages", "/api/messages/discussions", "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk",
- "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", "/api/_google", "/api/signup", "/api/inbox", "/api/u/**", "/.well-known/webfinger", "/.well-known/x-nodeinfo2", "/rss/**", "/api/events").permitAll()
+ .antMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions",
+ "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk",
+ "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin",
+ "/api/_google", "/api/signup", "/api/inbox", "/api/events", "/api/info/**",
+ "/api/nodeinfo/2.0").permitAll()
.anyRequest().hasRole("USER")
.and()
.anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY)
@@ -122,19 +144,6 @@ public class SecurityConfig {
return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
}
- @Bean
- public CorsConfigurationSource corsConfigurationSource() {
- CorsConfiguration configuration = new CorsConfiguration();
-
- configuration.setAllowedOrigins(Collections.singletonList("*"));
- configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "OPTIONS", "DELETE"));
- configuration.setAllowedHeaders(Collections.singletonList("*"));
-
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
- source.registerCorsConfiguration("/api/**", configuration);
-
- return source;
- }
@Override
public void configure(WebSecurity web) {
web.debug(false);
@@ -182,6 +191,7 @@ public class SecurityConfig {
.anyRequest().permitAll()
.and()
.anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY)
+ .and().cors().configurationSource(corsConfigurationSource())
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.invalidSessionUrl("/")
diff --git a/src/main/java/com/juick/server/configuration/XMPPConfig.java b/src/main/java/com/juick/server/configuration/XMPPConfig.java
index 2feef286..f9b6f092 100644
--- a/src/main/java/com/juick/server/configuration/XMPPConfig.java
+++ b/src/main/java/com/juick/server/configuration/XMPPConfig.java
@@ -1,23 +1,13 @@
package com.juick.server.configuration;
-import com.juick.server.XMPPConnection;
-import com.juick.server.XMPPServer;
+import com.juick.server.XMPPManager;
import com.juick.server.xmpp.JidConverter;
-import com.juick.server.xmpp.iq.MessageQuery;
-import com.juick.server.xmpp.router.XMPPRouter;
-import com.juick.server.xmpp.s2s.BasicXmppSession;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.DependsOn;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
-import rocks.xmpp.core.session.Extension;
-import rocks.xmpp.core.session.XmppSessionConfiguration;
-import rocks.xmpp.core.session.debug.LogbackDebugger;
-
-import java.time.Duration;
@Configuration
@ConditionalOnProperty("xmppbot_jid")
@@ -25,31 +15,13 @@ public class XMPPConfig {
@Value("${hostname:localhost}")
private String hostname;
@Bean
- public BasicXmppSession session() {
- XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
- .extensions(Extension.of(com.juick.Message.class), Extension.of(MessageQuery.class))
- .debugger(LogbackDebugger.class)
- .defaultResponseTimeout(Duration.ofMillis(120000))
- .build();
- return BasicXmppSession.create(hostname, configuration);
- }
- @Bean
public static ConversionService conversionService() {
DefaultFormattingConversionService cs = new DefaultFormattingConversionService();
cs.addConverter(new JidConverter());
return cs;
}
@Bean
- public XMPPServer xmppServer() {
- return new XMPPServer();
- }
- @Bean
- public XMPPRouter xmppRouter() {
- return new XMPPRouter();
- }
- @Bean
- @DependsOn("xmppRouter")
- public XMPPConnection xmppConnection() {
- return new XMPPConnection();
+ public XMPPManager xmppConnection() {
+ return new XMPPManager();
}
}
diff --git a/src/main/java/com/juick/server/helpers/HeaderRequestInterceptor.java b/src/main/java/com/juick/server/helpers/HeaderRequestInterceptor.java
new file mode 100644
index 00000000..9121f5ce
--- /dev/null
+++ b/src/main/java/com/juick/server/helpers/HeaderRequestInterceptor.java
@@ -0,0 +1,26 @@
+package com.juick.server.helpers;
+
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+
+import java.io.IOException;
+
+public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor {
+
+ private final String headerName;
+
+ private final String headerValue;
+
+ public HeaderRequestInterceptor(String headerName, String headerValue) {
+ this.headerName = headerName;
+ this.headerValue = headerValue;
+ }
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
+ request.getHeaders().set(headerName, headerValue);
+ return execution.execute(request, body);
+ }
+}
diff --git a/src/main/java/com/juick/server/util/ImageUtils.java b/src/main/java/com/juick/server/util/ImageUtils.java
index d16faf8f..2f5d3292 100644
--- a/src/main/java/com/juick/server/util/ImageUtils.java
+++ b/src/main/java/com/juick/server/util/ImageUtils.java
@@ -108,7 +108,7 @@ public class ImageUtils {
}
}
}
- } catch (ImageReadException e) {
+ } catch (ImageReadException | IOException e) {
// failed to read metadata.
// nothing to do here, return image as is.
}
diff --git a/src/main/java/com/juick/server/www/VaryHandler.java b/src/main/java/com/juick/server/www/VaryHandler.java
new file mode 100644
index 00000000..5a1b86a6
--- /dev/null
+++ b/src/main/java/com/juick/server/www/VaryHandler.java
@@ -0,0 +1,14 @@
+package com.juick.server.www;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ModelAttribute;
+
+import javax.servlet.http.HttpServletResponse;
+
+@ControllerAdvice
+public class VaryHandler {
+ @ModelAttribute
+ public void setVaryResponseHeader(HttpServletResponse response) {
+ response.setHeader("Vary", "Accept-Language");
+ }
+}
diff --git a/src/main/java/com/juick/server/www/controllers/AnythingFilter.java b/src/main/java/com/juick/server/www/controllers/AnythingFilter.java
index cdbeafc0..57b298eb 100644
--- a/src/main/java/com/juick/server/www/controllers/AnythingFilter.java
+++ b/src/main/java/com/juick/server/www/controllers/AnythingFilter.java
@@ -60,7 +60,7 @@ public class AnythingFilter extends OncePerRequestFilter {
} else {
com.juick.User user = userService.getUserByName(anything);
if (!user.isAnonymous()) {
- ((HttpServletResponse) servletResponse).sendRedirect("/" + user.getName() + "/?before=" + before);
+ servletResponse.sendRedirect("/" + user.getName() + "/?before=" + before);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
diff --git a/src/main/java/com/juick/server/www/controllers/MessagesWWW.java b/src/main/java/com/juick/server/www/controllers/MessagesWWW.java
index 1c69db32..4410f591 100644
--- a/src/main/java/com/juick/server/www/controllers/MessagesWWW.java
+++ b/src/main/java/com/juick/server/www/controllers/MessagesWWW.java
@@ -43,7 +43,9 @@ import ru.sape.Sape;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
+import java.net.URI;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -97,7 +99,7 @@ public class MessagesWWW {
@CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie,
ModelMap model) throws IOException {
if (tag != null) {
- return "redirect:/tag/" + URLEncoder.encode(tag, CharEncoding.UTF_8);
+ return "redirect:/tag/" + URLEncoder.encode(tag, StandardCharsets.UTF_8);
}
com.juick.User visitor = UserUtils.getCurrentUser();
@@ -182,10 +184,9 @@ public class MessagesWWW {
}
model.addAttribute("nextpage", nextpage);
}
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape.isPresent() && visitor.isAnonymous() && queryString == null) {
+ UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequest().build();
+ URI requestURI = builder.toUri();
+ if (sape.isPresent() && visitor.isAnonymous()) {
String links = sape.get().getPageLinks(requestURI, sapeCookie).render();
model.addAttribute("links", links);
}
@@ -295,17 +296,16 @@ public class MessagesWWW {
nextpage += "&amp;show=" + paramShow;
}
if (paramSearch != null) {
- nextpage += "&amp;search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8);
+ nextpage += "&amp;search=" + URLEncoder.encode(paramSearch, StandardCharsets.UTF_8);
}
if (paramTag != null) {
- nextpage += "&amp;tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8);
+ nextpage += "&amp;tag=" + URLEncoder.encode(paramTag.getName(), StandardCharsets.UTF_8);
}
model.addAttribute("nextpage", nextpage);
}
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape.isPresent() && visitor.isAnonymous() && queryString == null) {
+ UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequest().build();
+ URI requestURI = builder.toUri();
+ if (sape.isPresent() && visitor.isAnonymous()) {
String links = sape.get().getPageLinks(requestURI, sapeCookie).render();
model.addAttribute("links", links);
}
@@ -313,7 +313,7 @@ public class MessagesWWW {
}
@GetMapping("/{uname}/tags")
- protected String doGetTags(@PathVariable String uname, ModelMap model) throws IOException {
+ protected String doGetTags(@PathVariable String uname, ModelMap model) {
com.juick.User user = userService.getUserByName(uname);
com.juick.User visitor = UserUtils.getCurrentUser();
if (visitor.isBanned()) {
@@ -332,7 +332,7 @@ public class MessagesWWW {
}
@GetMapping("/{uname}/friends")
- protected String doGetFriends(@PathVariable String uname, ModelMap model) throws IOException {
+ protected String doGetFriends(@PathVariable String uname, ModelMap model) {
com.juick.User user = userService.getUserByName(uname);
com.juick.User visitor = UserUtils.getCurrentUser();
if (visitor.isBanned()) {
@@ -444,13 +444,12 @@ public class MessagesWWW {
model.addAttribute("isSubscribed", tagService.isSubscribed(visitor, paramTag));
model.addAttribute("isInBL", tagService.isInBL(visitor, paramTag));
if (mids.size() >= 20) {
- String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8) + "?before=" + mids.get(mids.size() - 1);
+ String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), StandardCharsets.UTF_8) + "?before=" + mids.get(mids.size() - 1);
model.addAttribute("nextpage", nextpage);
}
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape.isPresent() && visitor.isAnonymous() && queryString == null) {
+ UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequest().build();
+ URI requestURI = builder.toUri();
+ if (sape.isPresent() && visitor.isAnonymous()) {
String links = sape.get().getPageLinks(requestURI, sapeCookie).render();
model.addAttribute("links", links);
}
@@ -591,10 +590,9 @@ public class MessagesWWW {
}
model.addAttribute("replies", replies);
model.addAttribute("showAdv", visitor.isAnonymous());
- UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build();
- String queryString = builder.getQuery();
- String requestURI = builder.toUri().getPath();
- if (sape.isPresent() && visitor.isAnonymous() && queryString == null) {
+ UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequest().build();
+ URI requestURI = builder.toUri();
+ if (sape.isPresent() && visitor.isAnonymous()) {
String links = sape.get().getPageLinks(requestURI, sapeCookie).render();
model.addAttribute("links", links);
}
diff --git a/src/main/java/com/juick/server/www/controllers/Settings.java b/src/main/java/com/juick/server/www/controllers/Settings.java
index 57984aef..d5a21d09 100644
--- a/src/main/java/com/juick/server/www/controllers/Settings.java
+++ b/src/main/java/com/juick/server/www/controllers/Settings.java
@@ -18,7 +18,6 @@ package com.juick.server.www.controllers;
import com.juick.User;
import com.juick.model.NotifyOpts;
-import com.juick.model.UserInfo;
import com.juick.server.util.HttpBadRequestException;
import com.juick.server.util.HttpUtils;
import com.juick.server.util.UserUtils;
@@ -60,8 +59,6 @@ import java.util.stream.IntStream;
public class Settings {
private static final Logger logger = LoggerFactory.getLogger(Settings.class);
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
@Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
private String tmpDir;
@Inject
@@ -158,16 +155,15 @@ public class Settings {
}
break;
case "about":
- UserInfo info = new UserInfo();
- info.setFullName(request.getParameter("fullname"));
- info.setCountry(request.getParameter("country"));
- info.setUrl(request.getParameter("url"));
- info.setDescription(request.getParameter("descr"));
+ visitor.setFullName(request.getParameter("fullname"));
+ visitor.setCountry(request.getParameter("country"));
+ visitor.setUrl(request.getParameter("url"));
+ visitor.setDescription(request.getParameter("descr"));
String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost();
if (StringUtils.isNotEmpty(avatarTmpPath)) {
imagesService.saveAvatar(avatarTmpPath, visitor.getUid());
}
- if (userService.updateUserInfo(visitor, info)) {
+ if (userService.updateUserInfo(visitor)) {
result = String.format("<p>Your info is updated.</p><p><a href='/%s/'>Back to blog</a>.</p>", visitor.getName());
}
break;
diff --git a/src/main/java/com/juick/server/www/controllers/SocialLogin.java b/src/main/java/com/juick/server/www/controllers/SocialLogin.java
index bc631a1a..59b1ec0b 100644
--- a/src/main/java/com/juick/server/www/controllers/SocialLogin.java
+++ b/src/main/java/com/juick/server/www/controllers/SocialLogin.java
@@ -79,6 +79,7 @@ public class SocialLogin {
@Inject
private ObjectMapper jsonMapper;
private ServiceBuilder facebookBuilder, twitterBuilder, vkBuilder;
+ private OAuth20Service facebookAuthService, vkAuthService;
@Value("${twitter_consumer_key:appid}")
private String twitterConsumerKey;
@@ -107,6 +108,16 @@ public class SocialLogin {
vkBuilder = new ServiceBuilder(VK_APPID);
UriComponentsBuilder facebookRedirectBuilder = UriComponentsBuilder.fromUriString(baseUri);
facebookRedirectUri = facebookRedirectBuilder.replacePath("/_fblogin").build().toUriString();
+ facebookAuthService = facebookBuilder
+ .apiSecret(FACEBOOK_SECRET)
+ .callback(facebookRedirectUri)
+ .scope("email")
+ .build(FacebookApi.instance());
+ vkAuthService = vkBuilder
+ .apiSecret(VK_SECRET)
+ .scope("friends,wall,offline")
+ .callback(VK_REDIRECT)
+ .build(VkontakteApi.instance());
}
@GetMapping("/_fblogin")
@@ -120,13 +131,7 @@ public class SocialLogin {
state = Utils.getPreviousPageByRequest(request).orElse("https://juick.com/");
}
crosspostService.addFacebookState(fbstate, state);
- OAuth20Service facebookAuthService = facebookBuilder
- .apiSecret(FACEBOOK_SECRET)
- .callback(facebookRedirectUri)
- .scope("email")
- .state(fbstate)
- .build(FacebookApi.instance());
- return "redirect:" + facebookAuthService.getAuthorizationUrl();
+ return "redirect:" + facebookAuthService.getAuthorizationUrl(fbstate);
}
String redirectUrl = crosspostService.verifyFacebookState(state);
@@ -134,31 +139,24 @@ public class SocialLogin {
logger.error("state is missing");
throw new HttpBadRequestException();
}
- OAuth20Service facebookService = facebookBuilder
- .apiKey(FACEBOOK_APPID)
- .apiSecret(FACEBOOK_SECRET)
- .callback(facebookRedirectUri)
- .scope("email")
- .state(state)
- .build(FacebookApi.instance());
- OAuth2AccessToken token = facebookService.getAccessToken(code);
- final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email");
- facebookService.signRequest(token, meRequest);
- String graph = facebookService.execute(meRequest).getBody();
+ OAuth2AccessToken token = facebookAuthService.getAccessToken(code);
+ final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v3.2/me?fields=id,name,link,verified,email");
+ facebookAuthService.signRequest(token, meRequest);
+ String graph = facebookAuthService.execute(meRequest).getBody();
if (StringUtils.isBlank(graph)) {
logger.error("FACEBOOK GRAPH ERROR");
throw new HttpBadRequestException();
}
User fb = jsonMapper.readValue(graph, User.class);
long fbID = NumberUtils.toLong(fb.getId(), 0);
- if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) {
- logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink());
+ if (fbID == 0 || StringUtils.isBlank(fb.getName())) {
+ logger.error("Missing required fields, id: {}, name: {}", fbID, fb.getName());
throw new HttpBadRequestException();
}
int uid = crosspostService.getUIDbyFBID(fbID);
if (uid > 0) {
- if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) {
+ if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName())) {
logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken());
throw new HttpBadRequestException();
}
@@ -166,22 +164,19 @@ public class SocialLogin {
c.setMaxAge(50 * 24 * 60 * 60);
response.addCookie(c);
return "redirect:" + redirectUrl;
- } else if (fb.getVerified()) {
- if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) {
+ } else {
+ if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName())) {
if (StringUtils.isNotEmpty(fb.getEmail())) {
- logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink());
+ logger.info("found {} for facebook user {}", fb.getEmail());
Integer userId = crosspostService.getUIDbyFBID(fbID);
if (!emailService.getEmails(userId, false).contains(fb.getEmail())) {
emailService.addEmail(userId, fb.getEmail());
}
}
- logger.info("email not found for facebook user {}", fb.getLink());
+ logger.info("email not found for facebook user {}", fb.getName());
throw new HttpBadRequestException();
}
return "redirect:/signup?type=fb&hash=" + state;
- } else {
- logger.error("Facebook account is not verified, id: {}", fbID);
- throw new HttpBadRequestException();
}
}
@GetMapping("/_twitter")
@@ -243,13 +238,7 @@ public class SocialLogin {
vkstate = UUID.randomUUID().toString();
Cookie c = new Cookie("vkstate", vkstate);
response.addCookie(c);
- OAuth20Service vkAuthService = vkBuilder
- .apiSecret(VK_SECRET)
- .scope("friends,wall,offline")
- .state(vkstate)
- .callback(VK_REDIRECT)
- .build(VkontakteApi.instance());
- return "redirect:" + vkAuthService.getAuthorizationUrl();
+ return "redirect:" + vkAuthService.getAuthorizationUrl(vkstate);
}
if (StringUtils.isBlank(vkstate) || !vkstate.equals(state)) {
@@ -259,16 +248,11 @@ public class SocialLogin {
c.setMaxAge(0);
response.addCookie(c);
}
-
- OAuth20Service vkService = vkBuilder
- .apiKey(VK_APPID)
- .apiSecret(VK_SECRET)
- .build(VkontakteApi.instance());
- OAuth2AccessToken token = vkService.getAccessToken(code);
+ OAuth2AccessToken token = vkAuthService.getAccessToken(code);
OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73");
- vkService.signRequest(token, meRequest);
- String graph = vkService.execute(meRequest).getBody();
+ vkAuthService.signRequest(token, meRequest);
+ String graph = vkAuthService.execute(meRequest).getBody();
com.juick.model.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0);
String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName();
diff --git a/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java b/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java
deleted file mode 100644
index 231696ec..00000000
--- a/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.juick.server.xmpp;
-
-import com.juick.server.XMPPServer;
-import com.juick.server.xmpp.helpers.XMPPStatus;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-import rocks.xmpp.addr.Jid;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.inject.Inject;
-import java.util.stream.Collectors;
-
-@RestController
-@ConditionalOnProperty("xmppbot_jid")
-public class XMPPStatusPage {
- @Inject
- private XMPPServer xmpp;
- @ApiIgnore
- @RequestMapping(method = RequestMethod.GET, value = "/api/xmpp-status", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
- public XMPPStatus xmppStatus() {
- XMPPStatus status = new XMPPStatus();
- if (xmpp != null) {
- status.setInbound(xmpp.getInConnections().stream().map(c -> c.from).flatMap(j -> j.stream().map(Jid::getDomain)).collect(Collectors.toList()));
- status.setOutbound(xmpp.getOutConnections().keySet().stream()
- .map(c -> c.to).map(Jid::getDomain).collect(Collectors.toList()));
- }
- return status;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java b/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java
deleted file mode 100644
index 99d89866..00000000
--- a/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.helpers;
-
-import com.juick.server.xmpp.s2s.ConnectionIn;
-import com.juick.server.xmpp.s2s.ConnectionOut;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * Created by vitalyster on 16.02.2017.
- */
-public class XMPPStatus {
- private List<String> inbound;
- private List<String> outbound;
-
- public List<String> getInbound() {
- return inbound;
- }
-
- public void setInbound(List<String> inbound) {
- this.inbound = inbound;
- }
-
- public List<String> getOutbound() {
- return outbound;
- }
-
- public void setOutbound(List<String> outbound) {
- this.outbound = outbound;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/Handshake.java b/src/main/java/com/juick/server/xmpp/router/Handshake.java
deleted file mode 100644
index 0bc501dd..00000000
--- a/src/main/java/com/juick/server/xmpp/router/Handshake.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.juick.server.xmpp.router;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * Created by vitalyster on 30.01.2017.
- */
-public class Handshake {
- private String value;
-
- public static Handshake parse(XmlPullParser parser) throws IOException, XmlPullParserException {
- parser.next();
- Handshake handshake = new Handshake();
- handshake.setValue(XmlUtils.getTagText(parser));
- return handshake;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- @Override
- public String toString() {
- StringBuilder str = new StringBuilder("<handshake");
- if (getValue() != null) {
- str.append(">").append(getValue()).append("</handshake>");
- } else {
- str.append("/>");
- }
- return str.toString();
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/Stream.java b/src/main/java/com/juick/server/xmpp/router/Stream.java
deleted file mode 100644
index 2154edf6..00000000
--- a/src/main/java/com/juick/server/xmpp/router/Stream.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Juick
- * Copyright (C) 2008-2011, Ugnich Anton
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.server.xmpp.router;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-import rocks.xmpp.addr.Jid;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.time.Instant;
-import java.util.UUID;
-
-/**
- *
- * @author Ugnich Anton
- */
-public abstract class Stream {
-
- public boolean isLoggedIn() {
- return loggedIn;
- }
-
- public void setLoggedIn(boolean loggedIn) {
- this.loggedIn = loggedIn;
- }
-
- public Jid from;
- public Jid to;
- private InputStream is;
- private OutputStream os;
- private XmlPullParserFactory factory;
- protected XmlPullParser parser;
- private OutputStreamWriter writer;
- StreamHandler streamHandler;
- private boolean loggedIn;
- private Instant created;
- private Instant updated;
- String streamId;
- private boolean secured;
-
- public Stream(final Jid from, final Jid to, final InputStream is, final OutputStream os) throws XmlPullParserException {
- this.from = from;
- this.to = to;
- this.is = is;
- this.os = os;
- factory = XmlPullParserFactory.newInstance();
- created = updated = Instant.now();
- streamId = UUID.randomUUID().toString();
- }
-
- public void restartStream() throws XmlPullParserException {
- parser = factory.newPullParser();
- parser.setInput(new InputStreamReader(is, StandardCharsets.UTF_8));
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- writer = new OutputStreamWriter(os, StandardCharsets.UTF_8);
- }
-
- public void connect() {
- try {
- restartStream();
- handshake();
- parse();
- } catch (XmlPullParserException e) {
- StreamError invalidXmlError = new StreamError("invalid-xml");
- send(invalidXmlError.toString());
- connectionFailed(new Exception(invalidXmlError.getCondition()));
- } catch (IOException e) {
- connectionFailed(e);
- }
- }
-
- public void setHandler(final StreamHandler streamHandler) {
- this.streamHandler = streamHandler;
- }
-
- public abstract void handshake() throws XmlPullParserException, IOException;
-
- public void logoff() {
- setLoggedIn(false);
- try {
- writer.flush();
- writer.close();
- //TODO close parser
- } catch (final Exception e) {
- connectionFailed(e);
- }
- }
-
- public void send(final String str) {
- try {
- updated = Instant.now();
- writer.write(str);
- writer.flush();
- } catch (final Exception e) {
- connectionFailed(e);
- }
- }
-
- private void parse() throws IOException, XmlPullParserException {
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- if (parser.getEventType() == XmlPullParser.IGNORABLE_WHITESPACE) {
- setUpdated();
- }
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- continue;
- }
- setUpdated();
- final String tag = parser.getName();
- switch (tag) {
- case "message":
- case "presence":
- case "iq":
- streamHandler.stanzaReceived(XmlUtils.parseToString(parser, false));
- break;
- case "error":
- StreamError error = StreamError.parse(parser);
- connectionFailed(new Exception(error.getCondition()));
- return;
- default:
- XmlUtils.skip(parser);
- break;
- }
- }
- }
-
- /**
- * This method is used to be called on a parser or a connection error.
- * It tries to close the XML-Reader and XML-Writer one last time.
- */
- private void connectionFailed(final Exception ex) {
- if (isLoggedIn()) {
- try {
- writer.close();
- //TODO close parser
- } catch (Exception e) {
- }
- }
- if (streamHandler != null) {
- streamHandler.fail(ex);
- }
- }
-
- public Instant getCreated() {
- return created;
- }
-
- public Instant getUpdated() {
- return updated;
- }
- public String getStreamId() {
- return streamId;
- }
-
- public boolean isSecured() {
- return secured;
- }
-
- public void setSecured(boolean secured) {
- this.secured = secured;
- }
-
- public void setUpdated() {
- this.updated = Instant.now();
- }
-
- public InputStream getInputStream() {
- return is;
- }
-
- public void setInputStream(InputStream is) {
- this.is = is;
- }
-
- public OutputStream getOutputStream() {
- return os;
- }
-
- public void setOutputStream(OutputStream os) {
- this.os = os;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java b/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java
deleted file mode 100644
index a58adfc5..00000000
--- a/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.juick.server.xmpp.router;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.xmlpull.v1.XmlPullParserException;
-import rocks.xmpp.addr.Jid;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.UUID;
-
-/**
- * Created by vitalyster on 30.01.2017.
- */
-public class StreamComponentServer extends Stream {
-
- private String streamId, secret;
-
- public String getStreamId() {
- return streamId;
- }
-
-
- public StreamComponentServer(InputStream is, OutputStream os, String password) throws XmlPullParserException {
- super(null, null, is, os);
- secret = password;
- streamId = UUID.randomUUID().toString();
- }
- @Override
- public void handshake() throws XmlPullParserException, IOException {
- parser.next();
- if (!parser.getName().equals("stream")
- || !parser.getNamespace(null).equals(StreamNamespaces.NS_COMPONENT_ACCEPT)
- || !parser.getNamespace("stream").equals(StreamNamespaces.NS_STREAM)) {
- throw new IOException("invalid stream");
- }
- Jid domain = Jid.of(parser.getAttributeValue(null, "to"));
- if (streamHandler.filter(null, domain)) {
- send(new XMPPError(XMPPError.Type.cancel, "forbidden").toString());
- throw new IOException("invalid domain");
- }
- from = domain;
- to = domain;
- send(String.format("<stream:stream xmlns:stream='%s' " +
- "xmlns='%s' from='%s' id='%s'>", StreamNamespaces.NS_STREAM, StreamNamespaces.NS_COMPONENT_ACCEPT, from.asBareJid().toEscapedString(), streamId));
- Handshake handshake = Handshake.parse(parser);
- boolean authenticated = handshake.getValue().equals(DigestUtils.sha1Hex(streamId + secret));
- setLoggedIn(authenticated);
- if (!authenticated) {
- send(new XMPPError(XMPPError.Type.cancel, "not-authorized").toString());
- streamHandler.fail(new IOException("stream:stream, failed authentication"));
- return;
- }
- send(new Handshake().toString());
- streamHandler.ready(this);
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/StreamError.java b/src/main/java/com/juick/server/xmpp/router/StreamError.java
deleted file mode 100644
index f731f039..00000000
--- a/src/main/java/com/juick/server/xmpp/router/StreamError.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.juick.server.xmpp.router;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-
-/**
- * Created by vitalyster on 03.02.2017.
- */
-public class StreamError {
-
- private String condition;
- private String text;
-
- public StreamError() {}
-
- public StreamError(String condition) {
- this.condition = condition;
- }
-
- public static StreamError parse(XmlPullParser parser) throws IOException, XmlPullParserException {
- StreamError streamError = new StreamError();
- final int initial = parser.getDepth();
- while (true) {
- int eventType = parser.next();
- if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initial + 1) {
- final String tag = parser.getName();
- final String xmlns = parser.getNamespace();
- if (tag.equals("text") && xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) {
- streamError.text = XmlUtils.getTagText(parser);
- } else if (xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) {
- streamError.condition = tag;
- } else {
- XmlUtils.skip(parser);
- }
- } else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initial) {
- break;
- }
- }
- return streamError;
- }
-
- public String getCondition() {
- return condition;
- }
-
- @Override
- public String toString() {
- return String.format("<stream:error><%s xmlns='%s'/></stream:error>", condition, StreamNamespaces.NS_XMPP_STREAMS);
- }
-
- public String getText() {
- return text;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java b/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java
deleted file mode 100644
index e8fc324f..00000000
--- a/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Juick
- * Copyright (C) 2008-2013, Ugnich Anton
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.server.xmpp.router;
-
-import java.io.IOException;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-/**
- *
- * @author Ugnich Anton
- */
-public class StreamFeatures {
-
- public static final int NOTAVAILABLE = -1;
- public static final int AVAILABLE = 0;
- public static final int REQUIRED = 1;
- public int STARTTLS = NOTAVAILABLE;
- public int ZLIB = NOTAVAILABLE;
- public int PLAIN = NOTAVAILABLE;
- public int DIGEST_MD5 = NOTAVAILABLE;
- public int REGISTER = NOTAVAILABLE;
- public int EXTERNAL = NOTAVAILABLE;
-
- public static StreamFeatures parse(final XmlPullParser parser) throws XmlPullParserException, IOException {
- StreamFeatures features = new StreamFeatures();
- final int initial = parser.getDepth();
- while (true) {
- int eventType = parser.next();
- if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initial + 1) {
- final String tag = parser.getName();
- final String xmlns = parser.getNamespace();
- if (tag.equals("starttls") && xmlns != null && xmlns.equals("urn:ietf:params:xml:ns:xmpp-tls")) {
- features.STARTTLS = AVAILABLE;
- while (parser.next() == XmlPullParser.START_TAG) {
- if (parser.getName().equals("required")) {
- features.STARTTLS = REQUIRED;
- } else {
- XmlUtils.skip(parser);
- }
- }
- } else if (tag.equals("compression") && xmlns != null && xmlns.equals("http://jabber.org/features/compress")) {
- while (parser.next() == XmlPullParser.START_TAG) {
- if (parser.getName().equals("method")) {
- final String method = XmlUtils.getTagText(parser).toUpperCase();
- if (method.equals("ZLIB")) {
- features.ZLIB = AVAILABLE;
- }
- } else {
- XmlUtils.skip(parser);
- }
- }
- } else if (tag.equals("mechanisms") && xmlns != null && xmlns.equals("urn:ietf:params:xml:ns:xmpp-sasl")) {
- while (parser.next() == XmlPullParser.START_TAG) {
- if (parser.getName().equals("mechanism")) {
- final String mechanism = XmlUtils.getTagText(parser).toUpperCase();
- if (mechanism.equals("PLAIN")) {
- features.PLAIN = AVAILABLE;
- } else if (mechanism.equals("DIGEST-MD5")) {
- features.DIGEST_MD5 = AVAILABLE;
- } else if (mechanism.equals("EXTERNAL")) {
- features.EXTERNAL = AVAILABLE;
- }
- } else {
- XmlUtils.skip(parser);
- }
- }
- } else if (tag.equals("register") && xmlns != null && xmlns.equals("http://jabber.org/features/iq-register")) {
- features.REGISTER = AVAILABLE;
- XmlUtils.skip(parser);
- } else {
- XmlUtils.skip(parser);
- }
- } else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initial) {
- break;
- }
- }
- return features;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/StreamHandler.java b/src/main/java/com/juick/server/xmpp/router/StreamHandler.java
deleted file mode 100644
index 048c61ec..00000000
--- a/src/main/java/com/juick/server/xmpp/router/StreamHandler.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.juick.server.xmpp.router;
-
-import rocks.xmpp.addr.Jid;
-
-/**
- * Created by vitalyster on 01.02.2017.
- */
-public interface StreamHandler {
- void ready(StreamComponentServer componentServer);
- void fail(final Exception ex);
- boolean filter(Jid from, Jid to);
- void stanzaReceived(String stanza);
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java b/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java
deleted file mode 100644
index 1b9b1965..00000000
--- a/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.juick.server.xmpp.router;
-
-public class StreamNamespaces {
- public static final String NS_STREAM = "http://etherx.jabber.org/streams";
- public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls";
- public static final String NS_DB = "jabber:server:dialback";
- public static final String NS_SERVER = "jabber:server";
- public static final String NS_COMPONENT_ACCEPT = "jabber:component:accept";
- public static final String NS_XMPP_STREAMS = "urn:ietf:params:xml:ns:xmpp-streams";
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/XMPPError.java b/src/main/java/com/juick/server/xmpp/router/XMPPError.java
deleted file mode 100644
index 0cf9a3bc..00000000
--- a/src/main/java/com/juick/server/xmpp/router/XMPPError.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Juick
- * Copyright (C) 2008-2013, ugnich
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.server.xmpp.router;
-
-import org.apache.commons.text.StringEscapeUtils;
-
-/**
- *
- * @author ugnich
- */
-public class XMPPError {
-
- public static final class Type {
-
- public static final String auth = "auth";
- public static final String cancel = "cancel";
- public static final String continue_ = "continue";
- public static final String modify = "modify";
- public static final String wait = "wait";
- }
- private final static String TagName = "error";
- public String by = null;
- private String type;
- private String condition;
- private String text = null;
-
- public XMPPError(String type, String condition) {
- this.type = type;
- this.condition = condition;
- }
-
- @Override
- public String toString() {
- StringBuilder str = new StringBuilder("<").append(TagName).append("");
- if (by != null) {
- str.append(" by=\"").append(StringEscapeUtils.escapeXml10(by)).append("\"");
- }
- if (type != null) {
- str.append(" type=\"").append(StringEscapeUtils.escapeXml10(type)).append("\"");
- }
-
- if (condition != null) {
- str.append(">");
- str.append("<").append(StringEscapeUtils.escapeXml10(condition)).append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"");
- if (text != null) {
- str.append(">").append(StringEscapeUtils.escapeXml10(text)).append("</").append(StringEscapeUtils.escapeXml10(condition))
- .append(">");
- } else {
- str.append("/>");
- }
- str.append("</").append(TagName).append(">");
- } else {
- str.append("/>");
- }
-
- return str.toString();
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java b/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java
deleted file mode 100644
index 6d67fa9c..00000000
--- a/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java
+++ /dev/null
@@ -1,220 +0,0 @@
-package com.juick.server.xmpp.router;
-
-import com.juick.server.XMPPServer;
-import com.juick.server.xmpp.s2s.BasicXmppSession;
-import com.juick.server.xmpp.s2s.CacheEntry;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import org.xmlpull.v1.XmlPullParserException;
-import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.stanza.model.IQ;
-import rocks.xmpp.core.stanza.model.Message;
-import rocks.xmpp.core.stanza.model.Presence;
-import rocks.xmpp.core.stanza.model.Stanza;
-import rocks.xmpp.core.stanza.model.server.ServerIQ;
-import rocks.xmpp.core.stanza.model.server.ServerMessage;
-import rocks.xmpp.core.stanza.model.server.ServerPresence;
-import rocks.xmpp.util.XmppUtils;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutorService;
-
-public class XMPPRouter implements StreamHandler {
- private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp");
-
- @Inject
- private ExecutorService service;
-
- private final List<StreamComponentServer> connections = Collections.synchronizedList(new ArrayList<>());
- private final List<CacheEntry> outCache = new CopyOnWriteArrayList<>();
-
- private ServerSocket listener;
-
- @Inject
- private BasicXmppSession session;
-
- @Value("${router_port:5347}")
- private int routerPort;
-
- @Inject
- private XMPPServer xmppServer;
-
- @PostConstruct
- public void init() {
- logger.info("component router initialized");
- service.submit(() -> {
- try {
- listener = new ServerSocket(routerPort);
- logger.info("component router listening on {}", routerPort);
- while (!listener.isClosed()) {
- if (Thread.currentThread().isInterrupted()) break;
- Socket socket = listener.accept();
- service.submit(() -> {
- try {
- StreamComponentServer client = new StreamComponentServer(socket.getInputStream(), socket.getOutputStream(), "secret");
- addConnectionIn(client);
- client.setHandler(this);
- client.connect();
- } catch (IOException e) {
- logger.error("component error", e);
- } catch (XmlPullParserException e) {
- e.printStackTrace();
- }
- });
- }
- } catch (SocketException e) {
- // shutdown
- } catch (IOException e) {
- logger.warn("io exception", e);
- }
- });
- }
-
- @PreDestroy
- public void close() throws Exception {
- if (!listener.isClosed()) {
- listener.close();
- }
- synchronized (getConnections()) {
- for (Iterator<StreamComponentServer> i = getConnections().iterator(); i.hasNext(); ) {
- StreamComponentServer c = i.next();
- c.logoff();
- i.remove();
- }
- }
- service.shutdown();
- logger.info("XMPP router destroyed");
- }
-
- private void addConnectionIn(StreamComponentServer c) {
- synchronized (getConnections()) {
- getConnections().add(c);
- }
- }
-
- private void sendOut(Stanza s) {
- try {
- StringWriter stanzaWriter = new StringWriter();
- XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter(
- session.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter));
- session.createMarshaller().marshal(s, xmppStreamWriter);
- xmppStreamWriter.flush();
- xmppStreamWriter.close();
- String xml = stanzaWriter.toString();
- logger.info("XMPPRouter (out): {}", xml);
- sendOut(s.getTo().getDomain(), xml);
- } catch (XMLStreamException | JAXBException e1) {
- logger.info("jaxb exception", e1);
- }
- }
-
- private void sendOut(String hostname, String xml) {
- boolean haveAnyConn = false;
-
- StreamComponentServer connOut = null;
- synchronized (getConnections()) {
- for (StreamComponentServer c : getConnections()) {
- if (c.to != null && c.to.getDomain().equals(hostname)) {
- if (c.isLoggedIn()) {
- connOut = c;
- break;
- } else {
- logger.info("bouncing stanza to {} component until it will be ready", hostname);
- boolean haveCache = false;
- for (CacheEntry entry : outCache) {
- if (entry.hostname != null && entry.hostname.equals(hostname)) {
- entry.xml += xml;
- entry.updated = Instant.now();
- haveCache = true;
- break;
- }
- }
- if (!haveCache) {
- outCache.add(new CacheEntry(Jid.of(hostname), xml));
- }
- }
- }
- }
- }
- if (connOut != null) {
- connOut.send(xml);
- return;
- }
- xmppServer.sendOut(Jid.of(hostname), xml);
-
- }
-
- public List<StreamComponentServer> getConnections() {
- return connections;
- }
-
- private Stanza parse(String xml) {
- try {
- Unmarshaller unmarshaller = session.createUnmarshaller();
- return (Stanza)unmarshaller.unmarshal(new StringReader(xml));
- } catch (JAXBException e) {
- logger.error("JAXB exception", e);
- }
- return null;
- }
- @Override
- public void stanzaReceived(String stanza) {
- Stanza input = parse(stanza);
- if (input instanceof Message) {
- sendOut(ServerMessage.from((Message)input));
- } else if (input instanceof IQ) {
- sendOut(ServerIQ.from((IQ)input));
- } else {
- sendOut(ServerPresence.from((Presence) input));
- }
- }
-
- public String getFromCache(Jid to) {
- final String[] cache = new String[1];
- outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> {
- cache[0] = c.xml;
- outCache.remove(c);
- });
- return cache[0];
- }
-
- @Override
- public void ready(StreamComponentServer componentServer) {
- logger.info("component {} ready", componentServer.to);
- String cache = getFromCache(componentServer.to);
- if (cache != null) {
- logger.debug("sending cache to {}", componentServer.to);
- componentServer.send(cache);
- }
- }
-
- @Override
- public void fail(Exception e) {
-
- }
-
- @Override
- public boolean filter(Jid jid, Jid jid1) {
- return false;
- }
-} \ No newline at end of file
diff --git a/src/main/java/com/juick/server/xmpp/router/XmlUtils.java b/src/main/java/com/juick/server/xmpp/router/XmlUtils.java
deleted file mode 100644
index 7579489f..00000000
--- a/src/main/java/com/juick/server/xmpp/router/XmlUtils.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Juick
- * Copyright (C) 2008-2011, Ugnich Anton
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.juick.server.xmpp.router;
-
-import java.io.IOException;
-
-import org.apache.commons.text.StringEscapeUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-/**
- *
- * @author Ugnich Anton
- */
-public class XmlUtils {
-
- public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
- String tag = parser.getName();
- while (parser.getName() != null && !(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) {
- }
- }
-
- public static String getTagText(XmlPullParser parser) throws XmlPullParserException, IOException {
- String ret = "";
- String tag = parser.getName();
-
- if (parser.next() == XmlPullParser.TEXT) {
- ret = parser.getText();
- }
-
- while (!(parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals(tag))) {
- parser.next();
- }
-
- return ret;
- }
-
- public static String parseToString(XmlPullParser parser, boolean skipXMLNS) throws XmlPullParserException, IOException {
- String tag = parser.getName();
- StringBuilder ret = new StringBuilder("<").append(tag);
-
- // skipXMLNS for xmlns="jabber:client"
-
- String ns = parser.getNamespace();
- if (!skipXMLNS && ns != null && !ns.isEmpty()) {
- ret.append(" xmlns=\"").append(ns).append("\"");
- }
-
- for (int i = 0; i < parser.getAttributeCount(); i++) {
- String attr = parser.getAttributeName(i);
- if ((!skipXMLNS || !attr.equals("xmlns")) && !attr.contains(":")) {
- ret.append(" ").append(attr).append("=\"").append(StringEscapeUtils.escapeXml10(parser.getAttributeValue(i))).append("\"");
- }
- }
- ret.append(">");
-
- while (!(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) {
- int event = parser.getEventType();
- if (event == XmlPullParser.START_TAG) {
- if (!parser.getName().contains(":")) {
- ret.append(parseToString(parser, false));
- } else {
- skip(parser);
- }
- } else if (event == XmlPullParser.TEXT) {
- ret.append(StringEscapeUtils.escapeXml10(parser.getText()));
- }
- }
-
- ret.append("</").append(tag).append(">");
- return ret.toString();
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java b/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java
deleted file mode 100644
index ae28f827..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.XmppException;
-import rocks.xmpp.core.session.XmppSession;
-import rocks.xmpp.core.session.XmppSessionConfiguration;
-import rocks.xmpp.core.stanza.model.IQ;
-import rocks.xmpp.core.stanza.model.Message;
-import rocks.xmpp.core.stanza.model.Presence;
-import rocks.xmpp.core.stanza.model.server.ServerIQ;
-import rocks.xmpp.core.stanza.model.server.ServerMessage;
-import rocks.xmpp.core.stanza.model.server.ServerPresence;
-import rocks.xmpp.core.stream.model.StreamElement;
-
-/**
- * Created by vitalyster on 06.02.2017.
- */
-public class BasicXmppSession extends XmppSession {
- protected BasicXmppSession(String xmppServiceDomain, XmppSessionConfiguration configuration) {
- super(xmppServiceDomain, configuration);
- }
-
- public static BasicXmppSession create(String xmppServiceDomain, XmppSessionConfiguration configuration) {
- BasicXmppSession session = new BasicXmppSession(xmppServiceDomain, configuration);
- notifyCreationListeners(session);
- return session;
- }
-
- @Override
- public void connect(Jid from) throws XmppException {
-
- }
-
- @Override
- public Jid getConnectedResource() {
- return null;
- }
-
- @Override
- protected StreamElement prepareElement(StreamElement element) {
- if (element instanceof Message) {
- element = ServerMessage.from((Message) element);
- } else if (element instanceof Presence) {
- element = ServerPresence.from((Presence) element);
- } else if (element instanceof IQ) {
- element = ServerIQ.from((IQ) element);
- }
-
- return element;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java b/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java
deleted file mode 100644
index 33e875bd..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-import rocks.xmpp.addr.Jid;
-
-import java.time.Instant;
-
-/**
- *
- * @author ugnich
- */
-public class CacheEntry {
-
- public Jid hostname;
- public Instant created;
- public Instant updated;
- public String xml;
-
- public CacheEntry(Jid hostname, String xml) {
- this.hostname = hostname;
- this.created = this.updated =Instant.now();
- this.xml = xml;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/Connection.java b/src/main/java/com/juick/server/xmpp/s2s/Connection.java
deleted file mode 100644
index 4fa8e741..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/Connection.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.juick.server.XMPPServer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
-import java.time.Instant;
-import java.util.UUID;
-
-/**
- *
- * @author ugnich
- */
-public class Connection {
-
- protected static final Logger logger = LoggerFactory.getLogger(Connection.class);
-
- public String streamID;
- public Instant created;
- public Instant updated;
- public long bytesLocal = 0;
- public long packetsLocal = 0;
- XMPPServer xmpp;
- private Socket socket;
- public static final String NS_DB = "jabber:server:dialback";
- public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls";
- public static final String NS_SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
- public static final String NS_STREAM = "http://etherx.jabber.org/streams";
- XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
- XmlPullParser parser = factory.newPullParser();
- OutputStreamWriter writer;
- private boolean secured = false;
- private boolean authenticated = false;
- private boolean trusted = false;
-
-
-
- public Connection(XMPPServer xmpp) throws XmlPullParserException {
- this.xmpp = xmpp;
- created = updated = Instant.now();
- }
-
- public void logParser() {
- if (streamID == null) {
- return;
- }
- String tag = "IN: <" + parser.getName();
- for (int i = 0; i < parser.getAttributeCount(); i++) {
- tag += " " + parser.getAttributeName(i) + "=\"" + parser.getAttributeValue(i) + "\"";
- }
- tag += ">...</" + parser.getName() + ">\n";
- logger.trace(tag);
- }
-
- public void sendStanza(String xml) {
- if (streamID != null) {
- logger.trace("OUT: {}\n", xml);
- }
- try {
- writer.write(xml);
- writer.flush();
- } catch (IOException e) {
- logger.error("send stanza failed", e);
- }
-
- updated = Instant.now();
- bytesLocal += xml.length();
- packetsLocal++;
- }
-
- public void closeConnection() {
- if (streamID != null) {
- logger.debug("closing stream {}", streamID);
- }
-
- try {
- writer.write("</stream:stream>");
- } catch (Exception e) {
- }
-
- try {
- writer.close();
- } catch (Exception e) {
- }
-
- try {
- socket.close();
- } catch (Exception e) {
- }
- }
-
- public boolean isSecured() {
- return secured;
- }
-
- public void setSecured(boolean secured) {
- this.secured = secured;
- }
-
- public void restartParser() throws XmlPullParserException, IOException {
- streamID = UUID.randomUUID().toString();
- parser = factory.newPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new InputStreamReader(socket.getInputStream()));
- writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8);
- }
-
- @JsonIgnore
- public Socket getSocket() {
- return socket;
- }
-
- public void setSocket(Socket socket) {
- this.socket = socket;
- }
-
- public boolean isAuthenticated() {
- return authenticated;
- }
-
- public void setAuthenticated(boolean authenticated) {
- this.authenticated = authenticated;
- }
-
- public boolean isTrusted() {
- return trusted;
- }
-
- public void setTrusted(boolean trusted) {
- this.trusted = trusted;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java b/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java
deleted file mode 100644
index 72c3ba8d..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-import com.juick.server.XMPPServer;
-import com.juick.server.xmpp.router.StreamError;
-import com.juick.server.xmpp.router.XmlUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import rocks.xmpp.addr.Jid;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.Socket;
-import java.net.SocketException;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.stream.Collectors;
-
-/**
- * @author ugnich
- */
-public class ConnectionIn extends Connection implements Runnable {
-
- final public List<Jid> from = new CopyOnWriteArrayList<>();
- public Instant received;
- public long packetsRemote = 0;
- ConnectionListener listener;
-
- public ConnectionIn(XMPPServer xmpp, Socket socket) throws XmlPullParserException, IOException {
- super(xmpp);
- this.setSocket(socket);
- restartParser();
- }
-
- @Override
- public void run() {
- try {
- parser.next(); // stream:stream
- updateTsRemoteData();
- if (!parser.getName().equals("stream")
- || !parser.getNamespace("stream").equals(NS_STREAM)) {
-// || !parser.getAttributeValue(null, "version").equals("1.0")
-// || !parser.getAttributeValue(null, "to").equals(Main.HOSTNAME)) {
- throw new Exception(String.format("stream from %s invalid", getSocket().getRemoteSocketAddress()));
- }
- streamID = parser.getAttributeValue(null, "id");
- if (streamID == null) {
- streamID = UUID.randomUUID().toString();
- }
- boolean xmppversionnew = parser.getAttributeValue(null, "version") != null;
- String from = parser.getAttributeValue(null, "from");
-
- if (Arrays.asList(xmpp.bannedHosts).contains(from)) {
- closeConnection();
- return;
- }
- sendOpenStream(from, xmppversionnew);
-
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- updateTsRemoteData();
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- continue;
- }
- logParser();
-
- packetsRemote++;
-
- String tag = parser.getName();
- if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) {
- String dfrom = parser.getAttributeValue(null, "from");
- String to = parser.getAttributeValue(null, "to");
- logger.debug("stream from {} to {} {} asking for dialback", dfrom, to, streamID);
- if (dfrom.endsWith(xmpp.getJid().toEscapedString()) && (dfrom.equals(xmpp.getJid().toEscapedString())
- || dfrom.endsWith("." + xmpp.getJid()))) {
- logger.warn("stream from {} is invalid", dfrom);
- break;
- }
- if (to != null && to.equals(xmpp.getJid().toEscapedString())) {
- String dbKey = XmlUtils.getTagText(parser);
- updateTsRemoteData();
- xmpp.startDialback(Jid.of(dfrom), streamID, dbKey);
- } else {
- logger.warn("stream from " + dfrom + " " + streamID + " invalid to " + to);
- break;
- }
- } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) {
- String vfrom = parser.getAttributeValue(null, "from");
- String vto = parser.getAttributeValue(null, "to");
- String vid = parser.getAttributeValue(null, "id");
- String vkey = XmlUtils.getTagText(parser);
- updateTsRemoteData();
- final boolean[] valid = {false};
- if (vfrom != null && vto != null && vid != null && vkey != null) {
- xmpp.getConnectionOut(Jid.of(vfrom), false).ifPresent(c -> {
- String dialbackKey = c.dbKey;
- valid[0] = vkey.equals(dialbackKey);
- });
- }
- if (valid[0]) {
- sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='valid'/>");
- logger.debug("stream from {} {} dialback verify valid", vfrom, streamID);
- setAuthenticated(true);
- } else {
- sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='invalid'/>");
- logger.warn("stream from {} {} dialback verify invalid", vfrom, streamID);
- }
- } else if (tag.equals("presence") && checkFromTo(parser) && isAuthenticated()) {
- String xml = XmlUtils.parseToString(parser, false);
- logger.debug("stream {} presence: {}", streamID, xml);
- xmpp.onStanzaReceived(xml);
- } else if (tag.equals("message") && checkFromTo(parser)) {
- updateTsRemoteData();
- String xml = XmlUtils.parseToString(parser, false);
- logger.debug("stream {} message: {}", streamID, xml);
- xmpp.onStanzaReceived(xml);
-
- } else if (tag.equals("iq") && checkFromTo(parser) && isAuthenticated()) {
- updateTsRemoteData();
- String type = parser.getAttributeValue(null, "type");
- String xml = XmlUtils.parseToString(parser, false);
- if (type == null || !type.equals("error")) {
- logger.debug("stream {} iq: {}", streamID, xml);
- xmpp.onStanzaReceived(xml);
- }
- } else if (!isSecured() && tag.equals("starttls") && !isAuthenticated()) {
- listener.starttls(this);
- } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) {
- sendOpenStream(null, true);
- } else if (isSecured() && tag.equals("auth") && parser.getNamespace().equals(NS_SASL)
- && parser.getAttributeValue(null, "mechanism").equals("EXTERNAL")
- && !isAuthenticated() && isTrusted()) {
- sendStanza("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
- logger.info("stream {} authenticated externally", streamID);
- this.from.add(Jid.of(from));
- setAuthenticated(true);
- restartParser();
- } else if (tag.equals("error")) {
- StreamError streamError = StreamError.parse(parser);
- logger.debug("Stream error {} from {}: {}", streamError.getCondition(), streamID, streamError.getText());
- xmpp.removeConnectionIn(this);
- closeConnection();
- } else {
- String unhandledStanza = XmlUtils.parseToString(parser, true);
- logger.warn("Unhandled stanza from {}: {}", streamID, unhandledStanza);
- }
- }
- logger.warn("stream {} finished", streamID);
- xmpp.removeConnectionIn(this);
- closeConnection();
- } catch (EOFException | SocketException ex) {
- logger.debug("stream {} closed (dirty)", streamID);
- xmpp.removeConnectionIn(this);
- closeConnection();
- } catch (Exception e) {
- logger.debug("stream {} error {}", streamID, e);
- xmpp.removeConnectionIn(this);
- closeConnection();
- }
- }
-
- void updateTsRemoteData() {
- received = Instant.now();
- }
-
- void sendOpenStream(String from, boolean xmppversionnew) throws IOException {
- String openStream = "<?xml version='1.0'?><stream:stream xmlns='jabber:server' " +
- "xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" +
- xmpp.getJid().toEscapedString() + "' id='" + streamID + "' version='1.0'>";
- if (xmppversionnew) {
- openStream += "<stream:features>";
- if (listener != null && listener.isTlsAvailable() && !Arrays.asList(xmpp.brokenSSLhosts).contains(from)) {
- if (!isSecured()) {
- openStream += "<starttls xmlns='" + NS_TLS + "'><optional/></starttls>";
- } else if (!isAuthenticated() && isTrusted()) {
- openStream += "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" +
- "<mechanism>EXTERNAL</mechanism>" +
- "</mechanisms>";
- }
- }
- openStream += "</stream:features>";
- }
- sendStanza(openStream);
- }
-
- public void sendDialbackResult(Jid sfrom, String type) {
- sendStanza("<db:result from='" + xmpp.getJid().toEscapedString() + "' to='" + sfrom + "' type='" + type + "'/>");
- if (type.equals("valid")) {
- from.add(sfrom);
- logger.debug("stream from {} {} ready", sfrom, streamID);
- setAuthenticated(true);
- }
- }
-
- boolean checkFromTo(XmlPullParser parser) throws Exception {
- String cfrom = parser.getAttributeValue(null, "from");
- String cto = parser.getAttributeValue(null, "to");
- if (StringUtils.isNotEmpty(cfrom) && StringUtils.isNotEmpty(cto)) {
- Jid jidfrom = Jid.of(cfrom);
- for (Jid aFrom : from) {
- if (aFrom.equals(Jid.of(jidfrom.getDomain()))) {
- return true;
- }
- }
- }
- logger.warn("rejected from {}, to {}, stream {}", cfrom, cto, from.stream().collect(Collectors.joining(",")));
- return false;
- }
- public void setListener(ConnectionListener listener) {
- this.listener = listener;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java b/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java
deleted file mode 100644
index 4c32b9ae..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.juick.server.xmpp.s2s;
-
-
-import com.juick.server.xmpp.router.StreamError;
-
-public interface ConnectionListener {
- boolean isTlsAvailable();
- void starttls(ConnectionIn connection);
- void proceed(ConnectionOut connection);
- void verify(ConnectionOut connection, String from, String type, String sid);
- void dialbackError(ConnectionOut connection, StreamError error);
- void finished(ConnectionOut connection, boolean dirty);
- void exception(ConnectionOut connection, Exception ex);
- void ready(ConnectionOut connection);
- boolean securing(ConnectionOut connection);
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java b/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java
deleted file mode 100644
index be485ab1..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-import com.juick.server.xmpp.router.Stream;
-import com.juick.server.xmpp.router.StreamError;
-import com.juick.server.xmpp.router.StreamFeatures;
-import com.juick.server.xmpp.router.XmlUtils;
-import com.juick.server.xmpp.s2s.util.DialbackUtils;
-import org.apache.commons.codec.Charsets;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.text.RandomStringGenerator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xmlpull.v1.XmlPullParser;
-import rocks.xmpp.addr.Jid;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.SocketException;
-import java.util.UUID;
-
-import static com.juick.server.xmpp.router.StreamNamespaces.NS_STREAM;
-import static com.juick.server.xmpp.s2s.Connection.NS_SASL;
-
-/**
- * @author ugnich
- */
-public class ConnectionOut extends Stream {
- protected static final Logger logger = LoggerFactory.getLogger(ConnectionOut.class);
- public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls";
- public static final String NS_DB = "jabber:server:dialback";
- private boolean secured = false;
- private boolean trusted = false;
- public boolean streamReady = false;
- String checkSID = null;
- String dbKey = null;
- private String streamID;
- ConnectionListener listener;
- RandomStringGenerator generator = new RandomStringGenerator.Builder().withinRange('a', 'z').build();
-
- public ConnectionOut(Jid from, Jid to, InputStream is, OutputStream os, String checkSID, String dbKey) throws Exception {
- super(from, to, is, os);
- this.to = to;
- this.checkSID = checkSID;
- this.dbKey = dbKey;
- if (dbKey == null) {
- this.dbKey = DialbackUtils.generateDialbackKey(generator.generate(15), to, from, streamID);
- }
- streamID = UUID.randomUUID().toString();
- }
-
- public void sendOpenStream() throws IOException {
- send("<?xml version='1.0'?><stream:stream xmlns='jabber:server' id='" + streamID +
- "' xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" +
- from.toEscapedString() + "' to='" + to.toEscapedString() + "' version='1.0'>");
- }
-
- void processDialback() throws Exception {
- if (checkSID != null) {
- sendDialbackVerify(checkSID, dbKey);
- }
- send("<db:result from='" + from.toEscapedString() + "' to='" + to.toEscapedString() + "'>" +
- dbKey + "</db:result>");
- }
-
- @Override
- public void handshake() {
- try {
- restartStream();
-
- sendOpenStream();
-
- parser.next(); // stream:stream
- streamID = parser.getAttributeValue(null, "id");
- if (streamID == null || streamID.isEmpty()) {
- throw new Exception("stream to " + to + " invalid first packet");
- }
-
- logger.debug("stream to {} {} open", to, streamID);
- boolean xmppversionnew = parser.getAttributeValue(null, "version") != null;
- if (!xmppversionnew) {
- processDialback();
- }
-
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- continue;
- }
-
- String tag = parser.getName();
- if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) {
- String type = parser.getAttributeValue(null, "type");
- if (type != null && type.equals("valid")) {
- streamReady = true;
- listener.ready(this);
- } else {
- logger.warn("stream to {} {} dialback fail", to, streamID);
- }
- XmlUtils.skip(parser);
- } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) {
- String from = parser.getAttributeValue(null, "from");
- String type = parser.getAttributeValue(null, "type");
- String sid = parser.getAttributeValue(null, "id");
- listener.verify(this, from, type, sid);
- XmlUtils.skip(parser);
- } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) {
- StreamFeatures features = StreamFeatures.parse(parser);
- if (listener != null && !secured && features.STARTTLS >= 0
- && listener.securing(this)) {
- logger.debug("stream to {} {} securing", to.toEscapedString(), streamID);
- send("<starttls xmlns=\"" + NS_TLS + "\" />");
- } else if (secured && features.EXTERNAL >=0) {
- String authid = Base64.encodeBase64String(from.toEscapedString().getBytes(Charsets.UTF_8));
- send(String.format("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='EXTERNAL'>%s</auth>", authid));
- } else if (secured && streamReady) {
- listener.ready(this);
- } else {
- processDialback();
- }
- } else if (tag.equals("proceed") && parser.getNamespace().equals(NS_TLS)) {
- listener.proceed(this);
- } else if (tag.equals("success") && parser.getNamespace().equals(NS_SASL)) {
- streamReady = true;
- restartStream();
- sendOpenStream();
- } else if (secured && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) {
- streamID = parser.getAttributeValue(null, "id");
- } else if (tag.equals("error")) {
- StreamError streamError = StreamError.parse(parser);
- listener.dialbackError(this, streamError);
- } else {
- String unhandledStanza = XmlUtils.parseToString(parser, false);
- logger.warn("Unhandled stanza from {} {} : {}", to, streamID, unhandledStanza);
- }
- }
- listener.finished(this, false);
- } catch (EOFException | SocketException eofex) {
- listener.finished(this, true);
- } catch (Exception e) {
- listener.exception(this, e);
- }
- }
-
- public void sendDialbackVerify(String sid, String key) {
- send("<db:verify from='" + from.toEscapedString() + "' to='" + to + "' id='" + sid + "'>" +
- key + "</db:verify>");
- }
- public void setListener(ConnectionListener listener) {
- this.listener = listener;
- }
-
- public String getStreamID() {
- return streamID;
- }
-
- public boolean isSecured() {
- return secured;
- }
-
- public void setSecured(boolean secured) {
- this.secured = secured;
- }
-
- public boolean isTrusted() {
- return trusted;
- }
-
- public void setTrusted(boolean trusted) {
- this.trusted = trusted;
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java b/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java
deleted file mode 100644
index 1367d333..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-import org.apache.commons.lang3.math.NumberUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.InetSocketAddress;
-import java.util.Hashtable;
-import java.util.Random;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
-
-/**
- *
- * @author ugnich
- */
-public class DNSQueries {
-
- private static final Logger logger = LoggerFactory.getLogger(DNSQueries.class);
-
- private static Random rand = new Random();
-
- public static InetSocketAddress getServerAddress(String hostname) {
-
- String host = hostname;
- int port = 5269;
-
- Hashtable<String, String> env = new Hashtable<>(5);
- env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
- try {
- DirContext ctx = new InitialDirContext(env);
- Attribute att = ctx.getAttributes("_xmpp-server._tcp." + hostname, new String[]{"SRV"}).get("SRV");
-
- if (att != null && att.size() > 0) {
- int i = rand.nextInt(att.size());
- String srv[] = att.get(i).toString().split(" ");
- port = NumberUtils.toInt(srv[2], 5269);
- host = srv[3];
- }
- ctx.close();
- } catch (NamingException e) {
- logger.debug("SRV record for {} is not resolved, falling back to A record", hostname);
- }
- return new InetSocketAddress(host, port);
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java b/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java
deleted file mode 100644
index 6932298f..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s;
-
-
-import rocks.xmpp.core.stanza.model.Stanza;
-
-/**
- * Created by vitalyster on 07.12.2016.
- */
-public interface StanzaListener {
- void stanzaReceived(Stanza xmlValue);
-}
diff --git a/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java b/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java
deleted file mode 100644
index d25dbad8..00000000
--- a/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008-2017, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.juick.server.xmpp.s2s.util;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.codec.digest.HmacAlgorithms;
-import org.apache.commons.codec.digest.HmacUtils;
-import rocks.xmpp.addr.Jid;
-
-/**
- * Created by vitalyster on 05.12.2016.
- */
-public class DialbackUtils {
- private DialbackUtils() {
- throw new IllegalStateException();
- }
-
- public static String generateDialbackKey(String secret, Jid to, Jid from, String id) {
- return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, DigestUtils.sha256(secret))
- .hmacHex(to.toEscapedString() + " " + from.toEscapedString() + " " + id);
- }
-}