aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/juick/server
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2020-04-04 01:15:01 +0300
committerGravatar Vitaly Takmazov2020-04-04 01:15:01 +0300
commita608baeed738894433aacfa041e2617f60ce959f (patch)
tree1e0de7056417ff0833ae3d4600de9fec6eb81631 /src/main/java/com/juick/server
parent7a2f89266c8f6337e4e81a2fd8488e0f80f4f9bd (diff)
Initialize all components from configuration
Diffstat (limited to 'src/main/java/com/juick/server')
-rw-r--r--src/main/java/com/juick/server/ActivityPubManager.java407
-rw-r--r--src/main/java/com/juick/server/CommandsManager.java594
-rw-r--r--src/main/java/com/juick/server/EmailManager.java202
-rw-r--r--src/main/java/com/juick/server/KeystoreManager.java92
-rw-r--r--src/main/java/com/juick/server/ServerManager.java186
-rw-r--r--src/main/java/com/juick/server/SignatureManager.java171
-rw-r--r--src/main/java/com/juick/server/TelegramBotManager.java465
-rw-r--r--src/main/java/com/juick/server/TopManager.java73
-rw-r--r--src/main/java/com/juick/server/TwitterManager.java118
-rw-r--r--src/main/java/com/juick/server/Utils.java45
-rw-r--r--src/main/java/com/juick/server/XMPPManager.java644
-rw-r--r--src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java72
-rw-r--r--src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java52
-rw-r--r--src/main/java/com/juick/server/configuration/ApiAppConfiguration.java49
-rw-r--r--src/main/java/com/juick/server/configuration/BaseWebConfiguration.java56
-rw-r--r--src/main/java/com/juick/server/configuration/MailConfiguration.java32
-rw-r--r--src/main/java/com/juick/server/configuration/SapeConfiguration.java39
-rw-r--r--src/main/java/com/juick/server/configuration/SecurityConfig.java224
-rw-r--r--src/main/java/com/juick/server/configuration/SignInWithAppleConfig.java45
-rw-r--r--src/main/java/com/juick/server/configuration/StorageConfiguration.java37
-rw-r--r--src/main/java/com/juick/server/configuration/TelegramConfig.java32
-rw-r--r--src/main/java/com/juick/server/configuration/WwwAppConfiguration.java139
-rw-r--r--src/main/java/com/juick/server/configuration/XMPPConfig.java42
-rw-r--r--src/main/java/com/juick/server/helpers/HeaderRequestInterceptor.java43
-rw-r--r--src/main/java/com/juick/server/helpers/annotation/UserCommand.java50
-rw-r--r--src/main/java/com/juick/server/util/HttpBadRequestException.java31
-rw-r--r--src/main/java/com/juick/server/util/HttpForbiddenException.java33
-rw-r--r--src/main/java/com/juick/server/util/HttpNotFoundException.java32
-rw-r--r--src/main/java/com/juick/server/util/HttpUtils.java115
-rw-r--r--src/main/java/com/juick/server/util/ImageUtils.java173
-rw-r--r--src/main/java/com/juick/server/util/TagUtils.java42
-rw-r--r--src/main/java/com/juick/server/util/WebUtils.java62
-rw-r--r--src/main/java/com/juick/server/xmpp/JidConverter.java32
-rw-r--r--src/main/java/com/juick/server/xmpp/iq/MessageQuery.java27
-rw-r--r--src/main/java/com/juick/server/xmpp/iq/package-info.java25
35 files changed, 0 insertions, 4481 deletions
diff --git a/src/main/java/com/juick/server/ActivityPubManager.java b/src/main/java/com/juick/server/ActivityPubManager.java
deleted file mode 100644
index 50af506b..00000000
--- a/src/main/java/com/juick/server/ActivityPubManager.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * Copyright (C) 2008-2020, 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.model.Message;
-import com.juick.model.Reaction;
-import com.juick.model.User;
-import com.juick.formatters.PlainTextFormatter;
-import com.juick.model.Tag;
-import com.juick.www.api.SystemActivity.ActivityType;
-import com.juick.www.api.activity.model.Context;
-import com.juick.www.api.activity.model.activities.*;
-import com.juick.www.api.activity.model.objects.Hashtag;
-import com.juick.www.api.activity.model.objects.Image;
-import com.juick.www.api.activity.model.objects.Mention;
-import com.juick.www.api.activity.model.objects.Note;
-import com.juick.www.api.activity.model.objects.Person;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.server.util.HttpUtils;
-import com.juick.service.MessagesService;
-import com.juick.service.SocialService;
-import com.juick.service.UserService;
-import com.juick.service.activities.*;
-import com.juick.service.component.*;
-import com.juick.util.MessageUtils;
-import com.mitchellbosecke.pebble.PebbleEngine;
-import com.mitchellbosecke.pebble.template.PebbleTemplate;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import org.springframework.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.annotation.Nonnull;
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-@Component
-public class ActivityPubManager implements ActivityListener, NotificationListener {
- private static final Logger logger = LoggerFactory.getLogger("ActivityPub");
- @Inject
- private SignatureManager signatureManager;
- @Inject
- private SocialService socialService;
- @Inject
- private UserService userService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private PebbleEngine pebbleEngine;
- @Value("${ap_base_uri:http://localhost:8080/}")
- private String baseUri;
- @Value("${service_user:juick}")
- private String serviceUsername;
-
- private User serviceUser;
-
- @PostConstruct
- public void init() {
- serviceUser = userService.getUserByName(serviceUsername);
- }
-
- @Override
- public void processFollowEvent(@Nonnull FollowEvent followEvent) {
- String acct = (String)followEvent.getRequest().getObject();
- logger.info("received follower request to {}", acct);
- User followedUser = socialService.getUserByAccountUri(acct);
- if (!followedUser.isAnonymous()) {
- // automatically accept follower requests
- Person me = (Person) signatureManager.getContext(URI.create(acct)).get();
- Person follower = (Person) signatureManager.getContext(URI.create(followEvent.getRequest().getActor())).get();
- Accept accept = new Accept();
- accept.setActor(me.getId());
- accept.setObject(followEvent.getRequest());
- try {
- signatureManager.post(me, follower, accept);
- socialService.addFollower(followedUser, follower.getId());
- logger.info("Follower added for {}", followedUser.getName());
- } catch (IOException e) {
- logger.info("activitypub exception", e);
- }
- }
- }
-
- @Override
- public void undoFollowEvent(UndoFollowEvent event) {
- String actor = event.getActor();
- String me = event.getObject();
- logger.info("{} stopping to follow {}", actor, me);
- User followedUser = socialService.getUserByAccountUri(me);
- if (!followedUser.isAnonymous()) {
- socialService.removeFollower(followedUser, actor);
- }
- }
-
- @Override
- public void deleteUserEvent(DeleteUserEvent event) {
- String acct = event.getUserUri();
- logger.info("Deleting {} from followers", acct);
- socialService.removeAccount(acct);
- }
-
- @Override
- public void deleteMessageEvent(DeleteMessageEvent event) {
- Message msg = event.getMessage();
- User user = msg.getUser();
- String userUri = personUri(user);
- Note note = makeNote(msg);
- Person me = (Person) signatureManager.getContext(URI.create(userUri)).get();
- socialService.getFollowers(user).forEach(acct -> {
- Person follower = (Person) signatureManager.getContext(URI.create(acct)).get();
- Delete delete = new Delete();
- delete.setId(note.getId());
- delete.setActor(me.getId());
- delete.setPublished(note.getPublished());
- delete.setObject(note);
- try {
- logger.info("Deletion to follower {}", follower.getId());
- signatureManager.post(me, follower, delete);
- } catch (IOException e) {
- logger.warn("activitypub exception", e);
- }
- });
- }
-
- @Override
- public void processAnnounceEvent(AnnounceEvent event) {
- UriComponents uriComponents = UriComponentsBuilder.fromUriString(event.getMessageUri()).build();
- List<String> segments = uriComponents.getPathSegments();
- if (segments.get(0).equals("n")) {
- String[] ids = segments.get(1).split("-", 2);
- if (ids.length == 2 && Integer.parseInt(ids[1]) == 0) {
- // only messages
- logger.info("{} recommends {}", event.getActorUri(), Integer.valueOf(ids[0]));
- messagesService.likeMessage(Integer.parseInt(ids[0]), 0, Reaction.LIKE, event.getActorUri());
- }
- }
- }
-
- @Override
- public void undoAnnounceEvent(UndoAnnounceEvent event) {
- UriComponents uriComponents = UriComponentsBuilder.fromUriString(event.getMessageUri()).build();
- List<String> segments = uriComponents.getPathSegments();
- if (segments.get(0).equals("n")) {
- String[] ids = segments.get(1).split("-", 2);
- if (ids.length == 2 && Integer.parseInt(ids[1]) == 0) {
- // only messages
- logger.info("{} stop recommending {}", event.getActorUri(), Integer.valueOf(ids[0]));
- messagesService.likeMessage(Integer.parseInt(ids[0]), 0, null, event.getActorUri());
- }
- }
- }
-
- @Override
- public void processUpdateEvent(UpdateEvent event) {
- String objectUri = event.getMessageUri();
- User user = event.getUser();
- String userUri = personUri(user);
- Person me = (Person) signatureManager.getContext(URI.create(userUri)).get();
- socialService.getFollowers(user).forEach(acct -> {
- Person follower = (Person) signatureManager.getContext(URI.create(acct)).get();
- Update update = new Update();
- update.setId(objectUri + "#update");
- update.setActor(me.getId());
- update.setObject(objectUri);
- try {
- logger.info("Update to follower {}", follower.getId());
- signatureManager.post(me, follower, update);
- } catch (IOException e) {
- logger.warn("activitypub exception", e);
- }
- });
- }
-
- @Override
- public void processSystemEvent(SystemEvent systemEvent) {
- ActivityType type = systemEvent.getActivity().getType();
- if (type.equals(ActivityType.message)) {
- processMessage(systemEvent.getActivity().getMessage());
- } else if (type.equals(ActivityType.like)) {
- if (systemEvent.getActivity().getFrom().equals(serviceUser)) {
- processTop(systemEvent.getActivity().getMessage());
- }
- }
- }
- private void processMessage(Message msg) {
- if (MessageUtils.isPM(msg) || msg.isService()) {
- return;
- }
- User user = msg.getUser();
- String userUri = personUri(user);
- Note note = makeNote(msg);
- Person me = (Person) signatureManager.getContext(URI.create(userUri)).get();
- Set<String> subscribers = new HashSet<>(socialService.getFollowers(user));
- if (MessageUtils.isReply(msg) && msg.getTo().getUri().toASCIIString().length() > 0) {
- String replier = msg.getTo().getUri().toASCIIString();
- subscribers.add(replier);
- List<String> cc = new ArrayList<>(note.getCc());
- cc.add(replier);
- note.setCc(cc);
- }
- subscribers.addAll(note.getCc());
- subscribers.forEach(acct -> {
- Optional<Context> context = signatureManager.getContext(URI.create(acct));
- if (context.isPresent() && context.get() instanceof Person) {
- Person follower = (Person)context.get();
- Create create = new Create();
- create.setId(note.getId());
- create.setActor(me.getId());
- create.setPublished(note.getPublished());
- create.setObject(note);
- try {
- signatureManager.post(me, follower, create);
- } catch (IOException e) {
- logger.warn("activitypub exception", e);
- }
- }
- });
- }
-
- public String inboxUri() {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath("/api/inbox").toUriString();
- }
-
- public String outboxUri(User user) {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath(String.format("/u/%s/blog/toc", user.getName())).toUriString();
- }
-
- public String personUri(User user) {
- if (user.getUri().toString().length() > 0) {
- return user.getUri().toASCIIString();
- }
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath(String.format("/u/%s", user.getName())).toUriString();
- }
- public String personWebUri(User user) {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath(String.format("/%s/", user.getName())).toUriString();
- }
-
- public String followersUri(User user) {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath(String.format("/u/%s/followers/toc", user.getName())).toUriString();
- }
-
- public String followingUri(User user) {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath(String.format("/u/%s/following/toc", user.getName())).toUriString();
- }
- public String messageUri(Message msg) {
- return messageUri(msg.getMid(), msg.getRid());
- }
- public String messageUri(int mid, int rid) {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- uri.replacePath(String.format("/n/%d-%d", mid, rid));
- return uri.toUriString();
- }
- public String tagUri(Tag tag) {
- UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
- return uri.replacePath(String.format("/t/%s", tag.getName())).toUriString();
- }
-
- public String postId(String messageUri) {
- UriComponents uri = UriComponentsBuilder.fromUriString(messageUri).build();
- return uri.getPath().substring(uri.getPath().lastIndexOf('/') + 1).replace("-", "/");
- }
-
- public Note makeNote(Message msg) {
- Note note = new Note();
- note.setId(messageUri(msg));
- note.setUrl(PlainTextFormatter.formatUrl(msg));
- note.setAttributedTo(personUri(msg.getUser()));
- if (MessageUtils.isReply(msg)) {
- if (msg.getReplyToUri().toASCIIString().length() > 0) {
- note.setInReplyTo(msg.getReplyToUri().toASCIIString());
- } else {
- note.setInReplyTo(messageUri(msg.getMid(), msg.getReplyto()));
- }
- }
- if (MessageUtils.isPM(msg)) {
- note.setTo(Collections.singletonList(personUri(msg.getTo())));
- } else {
- note.setTo(Collections.singletonList(Context.ACTIVITYSTREAMS_PUBLIC));
- note.setCc(Collections.singletonList(followersUri(msg.getUser())));
- }
- note.setPublished(msg.getCreated());
- if (StringUtils.isNotBlank(msg.getAttachmentType())) {
- Image attachment = new Image();
- attachment.setId(msg.getAttachment().getMedium().getUrl());
- attachment.setUrl(msg.getAttachment().getMedium().getUrl());
- attachment.setMediaType(HttpUtils.mediaType(msg.getAttachmentType()));
- note.setAttachment(Collections.singletonList(attachment));
- }
- note.setTags(msg.getTags().stream().map(t -> new Hashtag(tagUri(t), t.getName())).collect(Collectors.toList()));
- if (msg.getReplyToUri() != null && msg.getReplyToUri().toASCIIString().length() > 0) {
- Optional<Context> noteContext = signatureManager.getContext(msg.getReplyToUri());
- if (noteContext.isPresent()) {
- Note activity = (Note) noteContext.get();
- Optional<Context> personContext = signatureManager.getContext(URI.create(activity.getAttributedTo()));
- if (personContext.isPresent()) {
- Person person = (Person) personContext.get();
- note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername()));
- msg.getTo().setName(person.getPreferredUsername());
- note.setInReplyTo(activity.getInReplyTo());
- }
- }
- } else if (MessageUtils.isReply(msg)) {
- note.getTags().add(new Mention(personWebUri(msg.getTo()), msg.getTo().getName()));
- }
- MessageUtils.getGlobalMentions(msg).forEach(m -> {
- // @user@server.tld -> user@server.tld
- Optional<Context> personContext = signatureManager.discoverPerson(m.substring(1));
- if (personContext.isPresent()) {
- Person person = (Person) personContext.get();
- note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername()));
- List<String> cc = new ArrayList<>(note.getCc());
- cc.add(person.getId());
- note.setCc(cc);
- }
- });
- if (msg.isHtml()) {
- note.setContent(msg.getText());
- } else {
- PebbleTemplate noteTemplate = pebbleEngine.getTemplate("layouts/note");
- Map<String, Object> context = new HashMap<>();
- context.put("msg", msg);
- context.put("baseUri", baseUri);
- try {
- Writer writer = new StringWriter();
- noteTemplate.evaluate(writer, context);
- note.setContent(writer.toString());
- } catch (IOException e) {
- logger.warn("template not rendered, falling back");
- note.setContent(MessageUtils.formatMessage(StringUtils.defaultString(msg.getText())));
- }
- }
- return note;
- }
-
- @Override
- public void processPingEvent(PingEvent pingEvent) {
-
- }
-
- private void processTop(Message message) {
- Note note = makeNote(message);
- Announce announce = new Announce();
- announce.setId(note.getId() + "#top");
- announce.setActor(personUri(serviceUser));
- announce.setTo(Collections.singletonList(Context.ACTIVITYSTREAMS_PUBLIC));
- announce.setObject(note);
- Person me = (Person) signatureManager.getContext(URI.create(announce.getActor())).get();
- socialService.getFollowers(serviceUser).forEach(acct -> {
- var follower = signatureManager.getContext(URI.create(acct));
- follower.ifPresentOrElse((person) -> {
- try {
- logger.info("Announcing top: {}", message.getMid());
- signatureManager.post(me, (Person)person, announce);
- } catch (IOException e) {
- logger.warn("activitypub exception", e);
- }
- }, () -> logger.warn("Follower not found: {}", acct));
- });
- }
- public User personToUser(URI uri) throws HttpBadRequestException {
- Person person = (Person) signatureManager.getContext(uri).orElseThrow(HttpBadRequestException::new);
- User user = new User();
- user.setUri(URI.create(person.getId()));
- user.setName(person.getPreferredUsername());
- if (person.getIcon() != null) {
- user.setAvatar(person.getIcon().getUrl());
- }
- return user;
- }
-}
diff --git a/src/main/java/com/juick/server/CommandsManager.java b/src/main/java/com/juick/server/CommandsManager.java
deleted file mode 100644
index bf907855..00000000
--- a/src/main/java/com/juick/server/CommandsManager.java
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
- * 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
- * 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.model.Message;
-import com.juick.model.Tag;
-import com.juick.model.User;
-import com.juick.formatters.PlainTextFormatter;
-import com.juick.model.CommandResult;
-import com.juick.model.TagStats;
-import com.juick.www.api.SystemActivity;
-import com.juick.server.helpers.annotation.UserCommand;
-import com.juick.server.util.HttpUtils;
-import com.juick.www.WebApp;
-import com.juick.service.*;
-import com.juick.service.activities.DeleteMessageEvent;
-import com.juick.service.component.*;
-import com.juick.util.MessageUtils;
-import org.apache.commons.collections4.CollectionUtils;
-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.apache.commons.text.StringEscapeUtils;
-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.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-/**
- *
- * @author ugnich
- */
-@Component
-public class CommandsManager {
- private static final Logger logger = LoggerFactory.getLogger(CommandsManager.class);
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private TagService tagService;
- @Inject
- private PMQueriesService pmQueriesService;
- @Inject
- private ShowQueriesService showQueriesService;
- @Inject
- private PrivacyQueriesService privacyQueriesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
- @Inject
- private ImagesService imagesService;
- @Inject
- private WebApp webApp;
- @Inject
- private ActivityPubManager activityPubManager;
-
- public CommandResult processCommand(@Nonnull User user, String data, @Nonnull URI attachment) throws Exception {
- if (!user.isAnonymous()) {
- userService.updateLastSeen(user);
- }
- String strippedData = StringUtils.stripStart(data, null);
- if (strippedData.startsWith("?OTR")) {
- return CommandResult.fromString("?OTR Error: we are not using OTR");
- }
- String input = StringEscapeUtils.unescapeHtml4(MessageUtils.stripNonSafeUrls(strippedData));
- Optional<Method> cmd = MethodUtils.getMethodsListWithAnnotation(getClass(), UserCommand.class).stream()
- .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(),
- m.getAnnotation(UserCommand.class).patternFlags()).matcher(input).matches())
- .findFirst();
- if (cmd.isPresent()) {
- Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(),
- cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(input);
- List<String> groups = new ArrayList<>();
- while (matcher.find()) {
- for (int i = 1; i <= matcher.groupCount(); i++) {
- groups.add(matcher.group(i));
- }
- }
- CommandResult commandResult = (CommandResult) getClass().getMethod(cmd.get().getName(), User.class, URI.class, String[].class)
- .invoke(this, user, attachment, groups.toArray(new String[groups.size()]));
- if (StringUtils.isNotEmpty(commandResult.getText())) {
- return commandResult;
- }
- }
- Pair<String, List<Tag>> tags = tagService.fromString(input);
- if (tags.getRight().size() > 5) {
- return CommandResult.fromString("Sorry, 5 tags maximum.");
- }
- // new message
- String body = tags.getLeft().trim();
- if (body.length() > 4096) {
- return CommandResult.fromString("Sorry, 4096 characters maximum.");
- }
- boolean haveAttachment = StringUtils.isNotEmpty(attachment.toString());
- 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 (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 SystemEvent(this, SystemActivity.read(user, msg)));
- applicationEventPublisher.publishEvent(new SystemEvent(this,
- SystemActivity.message(user, msg, subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg))));
- return CommandResult.build(msg,
- "New message posted.\n#" + msg.getMid() + " https://juick.com/m/" + msg.getMid(),
- String.format("[New message](%s) posted", PlainTextFormatter.formatUrl(msg)));
- }
-
- @UserCommand(pattern = "^ping$", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "PING - returns you a PONG")
- public CommandResult commandPing(User user, URI attachment, String[] input) {
- applicationEventPublisher.publishEvent(new PingEvent(this, user));
- return CommandResult.fromString("PONG");
- }
-
- @UserCommand(pattern = "^help$", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "HELP - returns this help message")
- public CommandResult commandHelp(User user, URI attachment, String[] input) {
- return CommandResult.fromString(Arrays.stream(getClass().getDeclaredMethods())
- .filter(m -> m.isAnnotationPresent(UserCommand.class))
- .map(m -> m.getAnnotation(UserCommand.class).help())
- .collect(Collectors.joining("\n")));
- }
-
- @UserCommand(pattern = "^login$", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "LOGIN - log in to Juick website")
- public CommandResult commandLogin(User user_from, URI attachment, String[] input) {
- return CommandResult.fromString("http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid()));
- }
- @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username")
- public CommandResult commandPM(User user_from, URI attachment, String... arguments) {
- 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)) {
- Message jmsg = new Message();
- jmsg.setUser(user_from);
- jmsg.setTo(user_to);
- jmsg.setText(body);
- applicationEventPublisher.publishEvent(new SystemEvent(this,
- SystemActivity.message(user_from, jmsg, Collections.singletonList(user_to))));
- return CommandResult.fromString("Private message sent");
- }
- }
- }
- return CommandResult.fromString("Error");
- }
- @UserCommand(pattern = "^bl$", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "BL - Show your blacklist")
- public CommandResult commandBLShow(User user_from, URI attachment, String... arguments) {
- List<User> blusers = userService.getUserBLUsers(user_from.getUid());
- List<String> bltags = tagService.getUserBLTags(user_from.getUid());
-
- String txt = StringUtils.EMPTY;
- if (bltags.size() > 0) {
- for (String bltag : bltags) {
- txt += "*" + bltag + "\n";
- }
-
- if (blusers.size() > 0) {
- txt += "\n";
- }
- }
- if (blusers.size() > 0) {
- for (User bluser : blusers) {
- txt += "@" + bluser.getName() + "\n";
- }
- }
- if (txt.isEmpty()) {
- txt = "You don't have any users or tags in your blacklist.";
- }
-
- return CommandResult.fromString(txt);
- }
-
- @UserCommand(pattern = "^#\\+$", help = "#+ - Show last Juick messages")
- public CommandResult commandLast(User user_from, URI attachment, String... arguments) {
- return CommandResult.fromString("Last messages:\n"
- + printMessages(user_from, messagesService.getAll(user_from.getUid(), 0), true));
- }
-
- @UserCommand(pattern = "@", help = "@ - Show recommendations and popular personal blogs")
- public CommandResult commandUsers(User user_from, URI attachment, String... arguments) {
- StringBuilder msg = new StringBuilder();
- msg.append("Recommended blogs");
- List<String> recommendedUsers = showQueriesService.getRecommendedUsers(user_from);
- if (recommendedUsers.size() > 0) {
- for (String user : recommendedUsers) {
- msg.append("\n@").append(user);
- }
- } else {
- msg.append("\nNo recommendations now. Subscribe to more blogs. ;)");
- }
- msg.append("\n\nTop 10 personal blogs:");
- List<String> topUsers = showQueriesService.getTopUsers();
- if (topUsers.size() > 0) {
- for (String user : topUsers) {
- msg.append("\n@").append(user);
- }
- } else {
- msg.append("\nNo top users. Empty DB? ;)");
- }
- return CommandResult.fromString(msg.toString());
- }
- @UserCommand(pattern = "^bl\\s+@([^\\s\\n\\+]+)", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "BL @username - add @username to your blacklist")
- public CommandResult blacklistUser(User user_from, URI attachment, String... arguments) {
- User blUser = userService.getUserByName(arguments[0]);
- if (!blUser.isAnonymous()) {
- PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistUser(user_from, blUser);
- if (result == PrivacyQueriesService.PrivacyResult.Added) {
- return CommandResult.fromString("User added to your blacklist");
- } else {
- return CommandResult.fromString("User removed from your blacklist");
- }
- }
- return CommandResult.fromString("User not found");
- }
- @UserCommand(pattern = "^bl\\s\\*(\\S+)$", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "BL *tag - add *tag to your blacklist")
- public CommandResult blacklistTag(User user_from, URI attachment, String... arguments) {
- if (!user_from.isAnonymous()) {
- Tag tag = tagService.getTag(arguments[0], false);
- if (tag != null) {
- PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistTag(user_from, tag);
- if (result == PrivacyQueriesService.PrivacyResult.Added) {
- return CommandResult.fromString("Tag added to your blacklist");
- } else {
- return CommandResult.fromString("Tag removed from your blacklist");
- }
- }
- }
- return CommandResult.fromString("Tag not found");
- }
- @UserCommand(pattern = "\\*", help = "* - Show your tags")
- public CommandResult commandTags(User currentUser, URI attachment, String... args) {
- List<TagStats> tags = tagService.getUserTagStats(currentUser.getUid());
- String msg = "Your tags: (tag - messages)\n" +
- tags.stream()
- .map(t -> String.format("\n*%s - %d", t.getTag().getName(), t.getUsageCount())).collect(Collectors.joining());
- return CommandResult.fromString(msg);
- }
- @UserCommand(pattern = "S", help = "S - Show your subscriptions", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandSubscriptions(User currentUser, URI attachment, String... args) {
- List<User> friends = userService.getUserFriends(currentUser.getUid());
- List<String> tags = subscriptionService.getSubscribedTags(currentUser);
- String msg = friends.size() > 0 ? "You are subscribed to users:" + friends.stream().map(u -> "\n@" + u.getName())
- .collect(Collectors.joining())
- : "You are not subscribed to any user.";
- msg += tags.size() > 0 ? "\nYou are subscribed to tags:" + tags.stream().map(t -> "\n*" + t)
- .collect(Collectors.joining())
- : "\nYou are not subscribed to any tag.";
- return CommandResult.fromString(msg);
- }
- @UserCommand(pattern = "!", help = "! - Show your favorite messages")
- public CommandResult commandFavorites(User currentUser, URI attachment, String... args) {
- List<Integer> mids = messagesService.getUserRecommendations(currentUser.getUid(), 0);
- if (mids.size() > 0) {
- return CommandResult.fromString("Favorite messages: \n" + printMessages(currentUser, mids, false));
- }
- return CommandResult.fromString("No favorite messages, try to \"like\" something ;)");
- }
- @UserCommand(pattern = "^\\!\\s+#(\\d+)", help = "! #12345 - recommend message")
- public CommandResult commandRecommend(User user, URI attachment, String... arguments) {
- int mid = NumberUtils.toInt(arguments[0], 0);
- if (mid > 0) {
- Optional<Message> msg = messagesService.getMessage(mid);
- if (msg.isPresent()) {
- if (msg.get().getUser() == user) {
- return CommandResult.fromString("You can't recommend your own messages.");
- }
- MessagesService.RecommendStatus status = messagesService.recommendMessage(mid, user.getUid(), user.getUri().toASCIIString());
- switch (status) {
- case Added:
- applicationEventPublisher.publishEvent(new SystemEvent(this, SystemActivity.like(user, msg.get(),
- subscriptionService.getUsersSubscribedToUserRecommendations(
- user.getUid(), msg.get()))));
- return CommandResult.fromString("Message is added to your recommendations");
- case Deleted:
- return CommandResult.fromString("Message deleted from your recommendations.");
- }
- }
- return CommandResult.fromString("Message not found");
- }
- return CommandResult.fromString("Message not found");
- }
- // TODO: target notification
- @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @username - subscribe to user" +
- "\nU @username - unsubscribe from user", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandSubscribeUser(User user, URI attachment, String... args) {
- boolean subscribe = args[0].equalsIgnoreCase("s");
- User toUser = userService.getUserByName(args[1]);
- if (toUser.isAnonymous()) {
- return CommandResult.fromString("User not found");
- }
- if (subscribe) {
- if (subscriptionService.subscribeUser(user, toUser)) {
- // TODO: already subscribed case
- applicationEventPublisher.publishEvent(new SystemEvent(this,
- SystemActivity.follow(user, Collections.singletonList(toUser))));
- return CommandResult.fromString("Subscribed to @" + toUser.getName());
- }
- } else {
- if (subscriptionService.unSubscribeUser(user, toUser)) {
- return CommandResult.fromString("Unsubscribed from @" + toUser.getName());
- }
- return CommandResult.fromString("You were not subscribed to @" + toUser.getName());
- }
- return CommandResult.fromString("Error");
- }
- @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" +
- "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandSubscribeTag(User user, URI attachment, String... args) {
- boolean subscribe = args[0].equalsIgnoreCase("s");
- Tag tag = tagService.getTag(args[1], true);
- if (subscribe) {
- if (subscriptionService.subscribeTag(user, tag)) {
- return CommandResult.fromString("Subscribed");
- }
- } else {
- if (subscriptionService.unSubscribeTag(user, tag)) {
- return CommandResult.fromString("Unsubscribed from " + tag.getName());
- }
- return CommandResult.fromString("You were not subscribed to " + tag.getName());
- }
- return CommandResult.fromString("Error");
- }
- @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments" +
- "\nU #1234 - unsubscribe from comments", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandSubscribeMessage(@Nonnull User user, URI attachment, String... args) {
- boolean subscribe = args[0].equalsIgnoreCase("s");
- int mid = NumberUtils.toInt(args[1], 0);
- Optional<Message> msg = messagesService.getMessage(mid);
- if (msg.isPresent()) {
- if (subscribe) {
- if (subscriptionService.subscribeMessage(msg.get(), user)) {
- applicationEventPublisher.publishEvent(
- new SystemEvent(this, SystemActivity.read(user, msg.get())));
- return CommandResult.fromString("Subscribed");
- }
- } else {
- if (subscriptionService.unSubscribeMessage(mid, user.getUid())) {
- return CommandResult.fromString("Unsubscribed from #" + mid);
- }
- return CommandResult.fromString("You were not subscribed to #" + mid);
- }
- }
- return CommandResult.fromString("Error");
- }
- @UserCommand(pattern = "^(on|off)$", patternFlags = Pattern.CASE_INSENSITIVE,
- help = "ON/OFF - Enable/disable subscriptions delivery")
- public CommandResult commandOnOff(User user, URI attachment, String[] input) {
- UserService.ActiveStatus newStatus;
- String retValUpdated;
- if (input[0].toLowerCase().equals("on")) {
- newStatus = UserService.ActiveStatus.Active;
- retValUpdated = "XMPP notifications are activated";
- } else {
- newStatus = UserService.ActiveStatus.Inactive;
- retValUpdated = "XMPP notifications are disabled";
- }
- if (userService.getAllJIDs(user).stream().allMatch(jid -> userService.setActiveStatusForJID(jid, newStatus))) {
- return CommandResult.fromString(retValUpdated);
- }
- return CommandResult.fromString("Error");
- }
- @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$",
- help = "@username+ - Show user's info and last 20 messages")
- public CommandResult commandUser(User user, URI attachment, String... arguments) {
- User blogUser = userService.getUserByName(arguments[0]);
- int page = arguments[1].length();
- if (!blogUser.isAnonymous() && !blogUser.isBanned()) {
- List<Integer> mids = messagesService.getUserBlog(blogUser.getUid(), 0, 0);
- return CommandResult.fromString(String.format("Last messages from @%s:\n%s", arguments[0],
- printMessages(user, mids, false)));
- }
- return CommandResult.fromString("User not found");
- }
- @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)")
- public CommandResult commandShow(@Nonnull User user, URI attachment, String... arguments) {
- boolean showReplies = arguments[1].length() > 0;
- int mid = NumberUtils.toInt(arguments[0], 0);
- if (mid == 0) {
- return CommandResult.fromString("Error");
- }
- Optional<Message> msg = messagesService.getMessage(mid);
- if (msg.isPresent()) {
- if (showReplies) {
- List<Message> replies = messagesService.getReplies(user, mid);
- applicationEventPublisher.publishEvent(
- new SystemEvent(this, SystemActivity.read(user, msg.get())));
- replies.add(0, msg.get());
- return CommandResult.fromString(replies.stream()
- .map(PlainTextFormatter::formatPostSummary).collect(Collectors.joining("\n")));
- }
- return CommandResult.fromString(PlainTextFormatter.formatPost(msg.get()));
- }
- return CommandResult.fromString("Message not found");
- }
- @UserCommand(pattern = "^#(\\d+)\\/(\\d+)$", help = "#1234/5 - Show reply")
- public CommandResult commandShowReply(User user, URI attachment, String... arguments) {
- int mid = NumberUtils.toInt(arguments[0], 0);
- int rid = NumberUtils.toInt(arguments[1], 0);
- Message reply = messagesService.getReply(mid, rid);
- if (reply != null) {
- return CommandResult.fromString(PlainTextFormatter.formatPost(reply));
- }
- return CommandResult.fromString("Reply not found");
- }
- @UserCommand(pattern = "^\\*(\\S+)(\\+?)$", help = "*tag - Show last messages with tag")
- public CommandResult commandShowTag(User user, URI attachment, String... arguments) {
- if (StringUtils.isNotEmpty(attachment.toString())) {
- // new message with tag
- return CommandResult.fromString(StringUtils.EMPTY);
- }
- Tag tag = tagService.getTag(arguments[0], false);
- if (tag != null) {
- // TODO: synonyms
- List<Integer> mids = messagesService.getTag(tag.TID, user.getUid(), 0, 10);
- return CommandResult.fromString("Last messages with *" + tag.getName() + ":\n" + printMessages(user, mids, true));
- }
- return CommandResult.fromString("Tag not found");
- }
- @UserCommand(pattern = "^D #(\\d+)$", help = "D #1234 - Delete post", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandDeletePost(User user, URI attachment, String... args) {
- int mid = Integer.valueOf(args[0]);
- Optional<Message> message = messagesService.getMessage(mid);
- if (message.isPresent() && messagesService.deleteMessage(user.getUid(), mid)) {
- applicationEventPublisher.publishEvent(new DeleteMessageEvent(this, message.get()));
- return CommandResult.fromString("Message deleted");
- }
- return CommandResult.fromString("This is not your message");
- }
- @UserCommand(pattern = "^D #(\\d+)(\\.|\\-|\\/)(\\d+)$", help = "D #1234/5 - Delete comment", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandDeleteReply(User user, URI attachment, String... args) {
- int mid = Integer.parseInt(args[0]);
- int rid = Integer.parseInt(args[2]);
- if (messagesService.deleteReply(user.getUid(), mid, rid)) {
- return CommandResult.fromString("Reply deleted");
- } else {
- return CommandResult.fromString("This is not your reply");
- }
- }
- @UserCommand(pattern = "^(D L|DL|D LAST)$", help = "D L - Delete last message", patternFlags = Pattern.CASE_INSENSITIVE)
- public CommandResult commandDeleteLast(User user, URI attachment, String... args) {
- return CommandResult.fromString("Temporarily unavailable");
- }
- @UserCommand(pattern = "^\\?\\s+\\@([a-zA-Z0-9\\-\\.\\@]+)\\s+([\\s\\S]+)$", help = "? @user string - search in user messages")
- public CommandResult commandSearch(User user, URI attachment, String... args) {
- return CommandResult.fromString("Temporarily unavailable");
- }
- @UserCommand(pattern = "^\\?\\s+([\\s\\S]+)$", help = "? string - search in all messages")
- public CommandResult commandSearchAll(User user, URI attachment, String... args) {
- return CommandResult.fromString("Temporarily unavailable");
- }
- @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)")
- public CommandResult commandMyFeed(User user, URI attachment, String... arguments) {
- // number of # is the page count
- int page = arguments[0].length() - 1;
- List<Integer> mids = messagesService.getMyFeed(user.getUid(), page, false);
- if (mids.size() > 0) {
- return CommandResult.fromString("Your feed: \n" + printMessages(user, mids, true));
- }
- return CommandResult.fromString("Your feed is empty");
- }
- @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)?",
- help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message")
- public CommandResult EditOrReply(@Nonnull User user, @Nonnull URI attachment, String... args) throws Exception {
- int mid = NumberUtils.toInt(args[1]);
- int rid = NumberUtils.toInt(args[4], 0);
- String txt = StringUtils.defaultString(args[5]);
- Optional<Message> msg = messagesService.getMessage(mid);
- if (!msg.isPresent()) {
- return CommandResult.fromString("Message not found");
- }
- if (rid > 0) {
- Message reply = messagesService.getReply(mid, rid);
- if (reply == null) {
- return CommandResult.fromString("Reply not found");
- }
- }
- Pair<String, List<Tag>> messageTags = tagService.fromString(txt);
- if (user.getUid() == msg.get().getUser().getUid() && rid == 0 && messageTags.getRight().size() > 0) {
- if (!CollectionUtils.isEqualCollection(tagService.updateTags(mid, messageTags.getRight()), msg.get().getTags())) {
- return CommandResult.fromString("Tags are updated");
- } else {
- return CommandResult.fromString("Tags are NOT updated (5 tags maximum?)");
- }
- } else {
- if (txt.length() > 4096) {
- return CommandResult.fromString("Sorry, 4096 characters maximum.");
- }
- boolean haveAttachment = StringUtils.isNotEmpty(attachment.toString());
- String attachmentFName = null;
- String attachmentType = null;
- if (haveAttachment) {
- 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());
- }
- }
- }
- 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 (newrid > 0) {
- if (haveAttachment && attachmentProcessed) {
- String fname = String.format("%d-%d.%s", mid, newrid, attachmentType);
- imagesService.saveImageWithPreviews(attachmentFName, fname);
- }
- applicationEventPublisher.publishEvent(
- new SystemEvent(this, SystemActivity.read(user, msg.get())));
- Message original = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new);
- subscriptionService.subscribeMessage(original, user);
- Message reply = messagesService.getReply(mid, newrid);
- if (reply.getUser().isAnonymous()) {
- reply.setUser(activityPubManager.personToUser(reply.getUser().getUri()));
- } else {
- reply.getUser().setAvatar(webApp.getAvatarUrl(reply.getUser()));
- }
- applicationEventPublisher.publishEvent(
- new SystemEvent(this,
- SystemActivity.message(reply.getUser(), reply,
- subscriptionService.getUsersSubscribedToComments(original, reply))));
- return CommandResult.build(reply, "Reply posted.\n#" + mid + "/" + newrid + " "
- + "https://juick.com/m/" + mid + "#" + newrid,
- String.format("[Reply](%s) posted", PlainTextFormatter.formatUrl(reply)));
- } else {
- return CommandResult.fromString("Message is read-only");
- }
- }
- }
-
- String printMessages(User visitor, List<Integer> mids, boolean crop) {
- return messagesService.getMessages(visitor, mids).stream()
- .sorted(Collections.reverseOrder())
- .map(PlainTextFormatter::formatPostSummary).collect(Collectors.joining("\n\n"));
- }
-}
diff --git a/src/main/java/com/juick/server/EmailManager.java b/src/main/java/com/juick/server/EmailManager.java
deleted file mode 100644
index 522f1db6..00000000
--- a/src/main/java/com/juick/server/EmailManager.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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
- * 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.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.model.Message;
-import com.juick.model.User;
-import com.juick.www.api.SystemActivity;
-import com.juick.server.util.HttpBadRequestException;
-import com.juick.www.WebApp;
-import com.juick.service.EmailService;
-import com.juick.service.MessagesService;
-import com.juick.service.UserService;
-import com.juick.service.component.*;
-import com.juick.util.MessageUtils;
-import com.mitchellbosecke.pebble.PebbleEngine;
-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.event.EventListener;
-import org.springframework.scheduling.annotation.Async;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import javax.mail.MessagingException;
-import javax.mail.Multipart;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.MimeMultipart;
-import java.util.*;
-
-import static com.juick.formatters.PlainTextFormatter.formatPost;
-import static com.juick.formatters.PlainTextFormatter.formatUrl;
-
-public class EmailManager implements NotificationListener {
-
- public static final String MSGID_PATTERN = "\\.|@|<";
-
- private static final Logger logger = LoggerFactory.getLogger(EmailManager.class);
- @Inject
- private EmailService emailService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private PebbleEngine pebbleEngine;
- @Inject
- private ObjectMapper jsonMapper;
- @Inject
- private WebApp webApp;
- @Value("${service_email:}")
- private String serviceEmail;
-
- @Override
- public void processSystemEvent(@Nonnull SystemEvent systemEvent) {
- var activity = systemEvent.getActivity();
- var msg = activity.getMessage();
- var subscribers = activity.getTo();
- if (activity.getType().equals(SystemActivity.ActivityType.message)) {
- processMessage(msg, subscribers);
- }
- try {
- var eventHeader = Collections.singletonMap("X-Event-Version", "1.0");
- sendEmail("noreply@juick.com", serviceEmail, "New system event",
- jsonMapper.writeValueAsString(systemEvent.getActivity()), null, eventHeader);
- } catch (JsonProcessingException e) {
- logger.warn("JSON exception", e);
- }
- }
- private void processMessage(Message msg, List<User> subscribedUsers) {
- if (msg.isService()) {
- return;
- }
- if (MessageUtils.isPM(msg)) {
- String subject = String.format("Private message from %s", msg.getUser().getName());
- emailService.getEmails(msg.getTo().getUid(), true).forEach(email -> emailNotify(email, subject, msg));
- } else if (MessageUtils.isReply(msg)) {
- Message originalMessage = messagesService.getMessage(msg.getMid()).orElseThrow(IllegalStateException::new);
- String subject = String.format("New reply to %s", originalMessage.getUser().getName());
- subscribedUsers.stream()
- .flatMap(user -> emailService.getEmails(user.getUid(), true).stream())
- .forEach(email -> emailNotify(email, subject, msg));
- } else {
- String subject = String.format("New message from %s", msg.getUser().getName());
- subscribedUsers
- .forEach(user -> emailService.getEmails(user.getUid(), true)
- .forEach(email -> emailNotify(email, subject, msg)));
- }
- }
-
- @Override
- public void processPingEvent(PingEvent pingEvent) {
-
- }
-
- @Async
- @EventListener
- public void processMailVerificationEvent(MailVerificationEvent mailVerificationEvent) {
- if (!sendEmail("noreply@juick.com", mailVerificationEvent.getEmail(), "Juick authorization link",
- String.format("Follow link to attach this email to Juick account:\n" +
- "http://juick.com/settings?page=auth-email&code=%s\n\n" +
- "If you don't know, what this mean - just ignore this mail.\n", mailVerificationEvent.getCode()),
- StringUtils.EMPTY, Collections.emptyMap())) {
- throw new HttpBadRequestException();
- }
- }
- @Async
- @EventListener
- public void processAccountVerificationEvent(AccountVerificationEvent accountVerificationEvent) {
- String signupUrl = String.format("Follow this link to create Juick account: https://juick.com/signup?type=email&hash=%s", accountVerificationEvent.getCode());
- sendEmail("noreply@juick.com", accountVerificationEvent.getEmail(), "Juick registration", signupUrl, StringUtils.EMPTY, Collections.emptyMap());
- }
-
- private void emailNotify(String email, String subject, Message msg) {
- Map<String, String> headers = new HashMap<>();
- if (!MessageUtils.isPM(msg)) {
- headers.put("Message-ID", String.format("<%d.%d@juick.com>", msg.getMid(), msg.getRid()));
- }
- if (MessageUtils.isReply(msg)) {
- if (msg.getReplyto() > 0) {
- Message replyto = messagesService.getReply(msg.getMid(), msg.getReplyto());
- headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", replyto.getMid(), replyto.getRid()));
- } else {
- Message original = messagesService.getMessage(msg.getMid()).orElseThrow(IllegalStateException::new);
- headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", original.getMid(), original.getRid()));
- }
- }
- String plainText = webApp.renderPlaintext(formatPost(msg), formatUrl(msg)).orElseThrow(IllegalStateException::new);
- String hash = userService.getHashByUID(userService.getUserByEmail(email).getUid());
- String htmlText = webApp.renderHtml(MessageUtils.formatHtml(msg), formatUrl(msg), msg, hash).orElseThrow(IllegalStateException::new);
- sendEmail(StringUtils.EMPTY, email, subject, plainText, htmlText, headers);
- }
- public boolean sendEmail(String from, String to, String subject, String textPart, String htmlPart, Map<String, String> messageHeaders) {
- Properties prop = System.getProperties();
- prop.put("mail.smtp.starttls.enable", "true");
- Session session = Session.getDefaultInstance(prop);
- try {
- Transport transport = session.getTransport("smtp");
- MimeMessage message = new MimeMessage(session) {
- protected void updateMessageID() throws MessagingException {
- for (Map.Entry<String, String> entry: messageHeaders.entrySet()) {
- setHeader(entry.getKey(), entry.getValue());
- }
- }
- };
- String fromAddress = StringUtils.isNotEmpty(from) ? from : "juick@juick.com";
- message.setFrom(fromAddress);
- message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to));
- message.setSubject(subject);
- MimeBodyPart textBodyPart = new MimeBodyPart();
- textBodyPart.setContent(textPart, "text/plain; charset=UTF-8");
-
- Multipart multipart = new MimeMultipart("alternative");
- multipart.addBodyPart(textBodyPart);
- if (StringUtils.isNotBlank(htmlPart)) {
- MimeBodyPart htmlBodyPart = new MimeBodyPart();
- htmlBodyPart.setContent(htmlPart, "text/html; charset=UTF-8");
- multipart.addBodyPart(htmlBodyPart);
- }
- message.setContent(multipart);
- User emailUser = userService.getUserByEmail(to);
- if (!emailUser.isAnonymous()) {
- message.setHeader("List-Id", "Juick notifications <mail-notifications.juick.com>");
- message.setHeader("List-Post", "<mailto:juick@juick.com>");
- message.setHeader("List-Owner", "<mailto:support@juick.com>");
- message.setHeader("List-Archive", "<https://juick.com/>");
- message.setHeader("List-Unsubscribe",
- String.format("<mailto:unsubscribe@juick.com>,<https://juick.com/settings/unsubscribe?hash=%s>",
- userService.getHashByUID(emailUser.getUid())));
- message.setHeader("List-Unsubscribe-Post", "List-Unsubscribe=One-Click");
- }
- message.saveChanges();
- transport.connect();
- transport.sendMessage(message, message.getAllRecipients());
- return true;
- } catch (MessagingException ex) {
- logger.error("mail exception", ex);
- return false;
- }
- }
-}
diff --git a/src/main/java/com/juick/server/KeystoreManager.java b/src/main/java/com/juick/server/KeystoreManager.java
deleted file mode 100644
index 0a66c2c8..00000000
--- a/src/main/java/com/juick/server/KeystoreManager.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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
- * 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.www.api.activity.model.objects.Person;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.io.Resource;
-import org.springframework.util.Base64Utils;
-
-import javax.net.ssl.KeyManagerFactory;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.*;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.Arrays;
-
-public class KeystoreManager {
- private static final Logger logger = LoggerFactory.getLogger("ActivityPub");
-
- private String keystorePassword;
-
- private KeyStore ks;
-
- public KeystoreManager(Resource keystore, String keystorePassword) {
- this.keystorePassword = keystorePassword;
- try (InputStream ksIs = keystore.getInputStream()) {
- ks = KeyStore.getInstance("PKCS12");
- ks.load(ksIs, keystorePassword.toCharArray());
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
- .getDefaultAlgorithm());
- kmf.init(ks, keystorePassword.toCharArray());
- } catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException e) {
- logger.error("Keystore error", e);
- }
- }
-
- private KeyPair getKeyPair() {
- Key privateKey;
- try {
- privateKey = ks.getKey("1", keystorePassword.toCharArray());
- Certificate certificate = ks.getCertificate("1");
- return new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey);
- } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
- e.printStackTrace();
- }
- return null;
- }
- public PrivateKey getPrivateKey() {
- return getKeyPair().getPrivate();
- }
- public PublicKey getPublicKey() {
- return getKeyPair().getPublic();
- }
- public String getPublicKeyPem() {
- String[] key = Base64Utils.encodeToString(getKeyPair().getPublic().getEncoded()).split("(?<=\\G.{64})");
- return String.format("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
- String.join("\n", key));
- }
- public static PublicKey publicKeyOf(Person person) {
- String pubkeyPem = person.getPublicKey().getPublicKeyPem();
- String[] rawKey = pubkeyPem.split("\\n");
- String pubkeyData = String.join("", Arrays.asList(rawKey).subList(1, rawKey.length - 1));
- try{
- byte[] byteKey = Base64Utils.decodeFromString(pubkeyData);
- X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
- KeyFactory kf = KeyFactory.getInstance("RSA");
- return kf.generatePublic(X509publicKey);
- }
- catch(Exception e){
- e.printStackTrace();
- }
- return null;
- }
-}
diff --git a/src/main/java/com/juick/server/ServerManager.java b/src/main/java/com/juick/server/ServerManager.java
deleted file mode 100644
index 1f11a9fb..00000000
--- a/src/main/java/com/juick/server/ServerManager.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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
- * 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.model.Message;
-import com.juick.model.User;
-import com.juick.model.AnonymousUser;
-import com.juick.www.api.SystemActivity;
-import com.juick.service.MessagesService;
-import com.juick.service.SubscriptionService;
-import com.juick.service.UserService;
-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 javax.annotation.Nonnull;
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.stream.Collectors;
-
-/**
- * @author Ugnich Anton
- */
-@Component
-public class ServerManager implements NotificationListener {
- private static final Logger logger = LoggerFactory.getLogger("Session");
-
- @Inject
- private MessagesService messagesService;
- @Inject
- private SubscriptionService subscriptionService;
- @Inject
- private UserService userService;
- private final CopyOnWriteArrayList<EventSession> sessions = new CopyOnWriteArrayList<>();
-
- @Value("${service_user:juick}")
- private String serviceUsername;
-
- private User serviceUser;
-
- @PostConstruct
- public void init() {
- serviceUser = userService.getUserByName(serviceUsername);
- }
-
-
- private void onJuickPM(final User to, final Message jmsg) {
- messageEvent(jmsg, Arrays.asList(to, jmsg.getUser()));
- }
-
- private void onJuickMessagePost(final Message jmsg, List<User> subscribedUsers) {
- messageEvent(jmsg, subscribedUsers);
- messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE));
- }
-
- private void onJuickMessageReply(final Message jmsg, final List<User> subscribedUsers) {
- messageEvent(jmsg, subscribedUsers);
- messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE));
- }
-
- @Override
- public void processSystemEvent(@Nonnull SystemEvent systemEvent) {
- var activity = systemEvent.getActivity();
- var from = activity.getFrom();
- var message = activity.getMessage();
- var subscribers = activity.getTo();
- if (activity.getType().equals(SystemActivity.ActivityType.message)) {
- processMessage(from, message, subscribers);
- } else if (activity.getType().equals(SystemActivity.ActivityType.like)) {
- if (from.equals(serviceUser)) {
- processTop(message);
- }
- }
- }
- private void processMessage(User from, Message jmsg, List<User> subscribers) {
- List<User> subscribedUsers = ListUtils.union(subscribers, Collections.singletonList(jmsg.getUser()));
- if (jmsg.isService()) {
- logger.info("Message read event from {} for {}", from.getUid(), jmsg.getMid());
- readEvent(jmsg, Collections.singletonList(serviceUser));
- return;
- }
- if (MessageUtils.isPM(jmsg)) {
- onJuickPM(jmsg.getTo(), jmsg);
- } else if (!MessageUtils.isReply(jmsg)) {
- onJuickMessagePost(jmsg, subscribedUsers);
- } else {
- // to get quote and attachment
- Message op = messagesService.getMessage(jmsg.getMid()).orElseThrow(IllegalStateException::new);
- 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));
- }
-
- @Override
- public void processPingEvent(PingEvent pingEvent) {
-
- }
-
- private void processTop(Message msg) {
- User topUser = msg.getUser();
- topEvent(msg, Arrays.asList(topUser, serviceUser));
- }
- private void topEvent(Message msg, List<User> subscribers){
- sendSseEvent(msg, "top", subscribers);
- }
-
- private void readEvent(Message msg, List<User> subscribers){
- sendSseEvent(msg, "read", subscribers);
- }
-
- private void messageEvent(Message msg, List<User> subscribers) {
- sendSseEvent(msg, "msg", subscribers);
- }
-
- private void sendSseEvent(Message msg, String name, List<User> subscribers) {
- List<EventSession> deadEmitters = new ArrayList<>();
- this.sessions.stream().filter(s -> subscribers.contains(s.user)).forEach(session -> {
- try {
- SseEmitter.SseEventBuilder builder = SseEmitter.event()
- .name(name)
- .data(msg);
- session.getEmitter().send(builder);
- } catch (Exception e) {
- deadEmitters.add(session);
- }
- });
- this.sessions.removeAll(deadEmitters);
- }
-
- public static class EventSession {
- private final User user;
- private final SseEmitter emitter;
-
- public EventSession(User user, SseEmitter sseEmitter) {
- this.user = user;
- this.emitter = sseEmitter;
- }
-
- public User getUser() {
- return user;
- }
-
- public SseEmitter getEmitter() {
- return emitter;
- }
- }
-
- public CopyOnWriteArrayList<EventSession> getSessions() {
- return sessions;
- }
- @Scheduled(fixedRate = 30000)
- public void ping() {
- Message ping = new Message();
- ping.setService(true);
- sendSseEvent(ping, "ping", getSessions().stream().map(s -> s.user)
- .distinct().collect(Collectors.toList()));
- }
-}
diff --git a/src/main/java/com/juick/server/SignatureManager.java b/src/main/java/com/juick/server/SignatureManager.java
deleted file mode 100644
index 602b4285..00000000
--- a/src/main/java/com/juick/server/SignatureManager.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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
- * 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.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.model.User;
-import com.juick.model.AnonymousUser;
-import com.juick.www.api.activity.model.Context;
-import com.juick.www.api.activity.model.objects.Person;
-import com.juick.www.api.webfinger.model.Account;
-import com.juick.www.api.webfinger.model.Link;
-import com.juick.service.UserService;
-import com.juick.util.DateFormattersHolder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.util.UriComponentsBuilder;
-import org.tomitribe.auth.signatures.Signature;
-import org.tomitribe.auth.signatures.Signer;
-import org.tomitribe.auth.signatures.Verifier;
-import rocks.xmpp.addr.Jid;
-
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URI;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
-import java.time.Instant;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-import static com.juick.www.api.activity.model.Context.ACTIVITY_MEDIA_TYPE;
-
-@Component
-public class SignatureManager {
- private static final Logger logger = LoggerFactory.getLogger("ActivityPub");
- @Inject
- private KeystoreManager keystoreManager;
- @Inject
- private ObjectMapper jsonMapper;
- @Inject
- private UserService userService;
- @Inject
- private RestTemplate apClient;
-
- public void post(Person from, Person to, Context data) throws IOException {
- UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(to.getInbox());
- URI inbox = uriComponentsBuilder.build().toUri();
- Instant now = Instant.now();
- String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now);
- 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("Host", host);
- requestHeaders.add("Signature", signatureString);
- HttpEntity<Context> request = new HttpEntity<>(Context.build(data), requestHeaders);
- logger.info("Sending context to {}: {}", to.getId(), jsonMapper.writeValueAsString(data));
- ResponseEntity<Void> response = apClient.postForEntity(inbox, request, Void.class);
- logger.info("Remote response: {}", 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 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);
- try {
- boolean result = verifier.verify(method, path, headers);
- 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.warn("Invalid signature {}", signatureString);
- }
- } else {
- logger.warn("Unknown keyId");
- }
- return AnonymousUser.INSTANCE;
- }
- public Optional<Context> getContext(URI contextUri) {
- 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.empty();
- }
- public Optional<Context> discoverPerson(String acct) {
- Jid acctId = Jid.of(acct);
- URI resourceUri = UriComponentsBuilder.fromPath("/.well-known/webfinger")
- .host(acctId.getDomain())
- .scheme("https")
- .queryParam("resource", String.format("%s", acctId.toEscapedString())).build().toUri();
- HttpHeaders headers = new HttpHeaders();
- headers.add("Accept", "application/jrd+json");
- HttpEntity<Void> webfingerRequest = new HttpEntity<>(headers);
- ResponseEntity<Account> response = apClient.exchange(
- resourceUri, HttpMethod.GET, webfingerRequest, Account.class);
- if (response.getStatusCode().is2xxSuccessful()) {
- Account acctData = response.getBody();
- if (acctData != null) {
- for (Link l : acctData.getLinks()) {
- if (l.getRel().equals("self") && l.getType().equals(ACTIVITY_MEDIA_TYPE)) {
- return getContext(URI.create(l.getHref()));
- }
- }
- }
- }
- return Optional.empty();
- }
-}
diff --git a/src/main/java/com/juick/server/TelegramBotManager.java b/src/main/java/com/juick/server/TelegramBotManager.java
deleted file mode 100644
index 3c38e5de..00000000
--- a/src/main/java/com/juick/server/TelegramBotManager.java
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * 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
- * 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.model.User;
-import com.juick.model.AnonymousUser;
-import com.juick.model.CommandResult;
-import com.juick.www.api.SystemActivity;
-import com.juick.server.util.HttpUtils;
-import com.juick.service.MessagesService;
-import com.juick.service.TelegramService;
-import com.juick.service.UserService;
-import com.juick.service.component.SystemEvent;
-import com.juick.service.component.NotificationListener;
-import com.juick.service.component.PingEvent;
-import com.juick.util.MessageUtils;
-import com.pengrad.telegrambot.Callback;
-import com.pengrad.telegrambot.TelegramBot;
-import com.pengrad.telegrambot.UpdatesListener;
-import com.pengrad.telegrambot.model.Message;
-import com.pengrad.telegrambot.model.MessageEntity;
-import com.pengrad.telegrambot.model.PhotoSize;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.GetFile;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendPhoto;
-import com.pengrad.telegrambot.request.SetWebhook;
-import com.pengrad.telegrambot.response.GetFileResponse;
-import com.pengrad.telegrambot.response.SendResponse;
-import org.apache.commons.lang3.StringUtils;
-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.web.util.UriComponents;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.annotation.Nonnull;
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URL;
-import java.util.*;
-
-import static com.juick.formatters.PlainTextFormatter.formatPost;
-import static com.juick.formatters.PlainTextFormatter.formatUrl;
-
-/**
- * Created by vt on 12/05/16.
- */
-public class TelegramBotManager implements NotificationListener {
- private static final Logger logger = LoggerFactory.getLogger("Telegram");
-
- private TelegramBot bot;
-
- @Value("${telegram_api_url:}")
- private String apiUrl;
- @Value("${telegram_file_api_url:}")
- private String fileApiUrl;
- @Value("${telegram_webhook_url:}")
- private String webhookUrl;
- @Value("${telegram_token:12345678}")
- private String telegramToken;
- @Value("${telegram_debug:false}")
- private boolean telegramDebug;
- @Inject
- private TelegramService telegramService;
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private CommandsManager commandsManager;
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
- @Value("${service_user:juick}")
- private String serviceUser;
-
- private static final String MSG_LINK = "🔗";
-
- @PostConstruct
- public void init() {
- TelegramBot.Builder tgBuilder = new TelegramBot.Builder(telegramToken);
- if (StringUtils.isNotEmpty(apiUrl)) {
- tgBuilder.apiUrl(apiUrl).fileApiUrl(fileApiUrl);
- }
- bot = tgBuilder.build();
- if (!telegramDebug) {
- try {
- SetWebhook webhook = new SetWebhook().url(webhookUrl);
- if (!bot.execute(webhook).isOk()) {
- logger.error("error setting webhook");
- }
- } catch (Exception e) {
- logger.warn("couldn't initialize telegram bot", e);
- }
- } else {
- bot.setUpdatesListener(updates -> {
- logger.info("got updates: {}", updates);
- updates.forEach(this::processUpdate);
- return UpdatesListener.CONFIRMED_UPDATES_ALL;
- });
- }
- }
-
- public void processUpdate(Update update) {
- Message message = update.message();
- if (update.message() == null) {
- message = update.editedMessage();
- if (message == null) {
- logger.error("error parsing telegram update: {}", update);
- return;
- }
- User user_from = userService.getUserByUID(telegramService.getUser(message.chat().id())).orElse(AnonymousUser.INSTANCE);
- logger.info("Found juick user {}", user_from.getUid());
- Optional<Pair<Integer, Integer>> originalMessageData = messagesService.findMessageByProperty("durovId",
- String.valueOf(message.messageId()));
- if (originalMessageData.isPresent()) {
- int mid = originalMessageData.get().getLeft();
- int rid = originalMessageData.get().getRight();
- // TODO: this is copypaste from api, need switch to api
- com.juick.model.Message originalMessage = rid == 0 ? messagesService.getMessage(mid).orElseThrow(IllegalStateException::new)
- : messagesService.getReply(mid, rid);
- User author = originalMessage.getUser();
- String newMessageText = StringUtils.defaultString(message.text());
- if (user_from.equals(author) && canUpdateMessage(originalMessage, newMessageText)) {
- if (messagesService.updateMessage(mid, rid, newMessageText)) {
- telegramNotify(message.chat().id(), "Message updated", new com.juick.model.Message());
- return;
- }
- }
- telegramNotify(message.chat().id(), "Error updating message", new com.juick.model.Message());
- }
- } else {
- User user_from = userService.getUserByUID(telegramService.getUser(message.chat().id())).orElse(AnonymousUser.INSTANCE);
- logger.info("Found juick user {}", user_from.getUid());
-
- String username = message.from().username();
- if (username == null) {
- username = message.from().firstName();
- }
- if (!user_from.isAnonymous()) {
- URI attachment = URI.create(StringUtils.EMPTY);
- if (message.photo() != null) {
- String fileId = Arrays.stream(message.photo()).max(Comparator.comparingInt(PhotoSize::fileSize))
- .orElse(new PhotoSize()).fileId();
- if (StringUtils.isNotEmpty(fileId)) {
- GetFile request = new GetFile(fileId);
- GetFileResponse response = bot.execute(request);
- logger.info("got file {}", response.file());
- try {
- URL fileURL = new URL(bot.getFullFilePath(response.file()));
- attachment = HttpUtils.downloadImage(fileURL, tmpDir);
- } catch (Exception e) {
- logger.warn("attachment exception", e);
- }
- logger.info("received {}", attachment);
- }
- }
- String text = message.text();
- if (StringUtils.isBlank(text)) {
- text = message.caption();
- }
- if (StringUtils.isBlank(text)) {
- text = StringUtils.EMPTY;
- }
- if (StringUtils.isNotEmpty(text) || StringUtils.isNotEmpty(attachment.toString())) {
- if (text.equalsIgnoreCase("LOGIN")
- || text.equalsIgnoreCase("PING")
- || text.equalsIgnoreCase("HELP")
- || text.equalsIgnoreCase("/login")
- || text.equalsIgnoreCase("/logout")
- || text.equalsIgnoreCase("/start")
- || text.equalsIgnoreCase("/help")) {
- String msgUrl = "http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid());
- String msg = String.format("Hi, %s!\nYou can post messages and images to Juick there.\n" +
- "Tap to [log into website](%s) to get more info", user_from.getName(), msgUrl);
- telegramNotify(message.from().id().longValue(), msg, new com.juick.model.Message());
- } else {
- Message replyMessage = message.replyToMessage();
- if (replyMessage != null) {
- MessageEntity[] entities = replyMessage.entities();
- if (entities == null) {
- entities = replyMessage.captionEntities();
- }
- if (entities != null) {
- Optional<MessageEntity> juickLink = Arrays.stream(entities)
- .filter(this::isJuickLink)
- .findFirst();
- if (juickLink.isPresent()) {
- if (StringUtils.isNotEmpty(juickLink.get().url())) {
- UriComponents uriComponents = UriComponentsBuilder.fromUriString(
- juickLink.get().url()).build();
- String path = uriComponents.getPath();
- if (StringUtils.isNotEmpty(path) && path.length() > 1) {
- int mid;
- try {
- mid = Integer.parseInt(path.substring(3));
- } catch (NumberFormatException e) {
- logger.warn("wrong mid received");
- return;
- }
- String prefix = String.format("#%d ", mid);
- if (StringUtils.isNotEmpty(uriComponents.getFragment())) {
- int rid = Integer.parseInt(uriComponents.getFragment());
- prefix = String.format("#%d/%d ", mid, rid);
- }
- executeCommand(message.messageId(), message.from().id().longValue(),
- user_from, prefix + text, attachment);
- } else {
- logger.warn("invalid path: {}", path);
- }
- } else {
- logger.warn("invalid entity: {}", juickLink);
- }
- } else {
- telegramNotify(message.from().id().longValue(),
- "Can not reply to this message", replyMessage.messageId(), new com.juick.model.Message());
- }
- } else {
- telegramNotify(message.from().id().longValue(),
- "Can not reply to this message", replyMessage.messageId(), new com.juick.model.Message());
- }
- } else {
- executeCommand(message.messageId(), message.from().id().longValue(),
- user_from, text, attachment);
- }
- }
- }
- messagesService.getUnread(user_from).forEach(mid -> messagesService.setRead(user_from, mid));
- } else {
- List<Long> chats = telegramService.getAnonymous();
- if (!chats.contains(message.chat().id())) {
- logger.info("added chat with {}", username);
- telegramService.createTelegramUser(message.from().id(), username);
- }
- telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username));
- }
- }
- }
-
- /*
- validate user input
- */
- private boolean canUpdateMessage(com.juick.model.Message message, String newData) {
- if (StringUtils.isEmpty(newData)) {
- // allow empty text only when image is present
- return StringUtils.isNotEmpty(message.getAttachmentType());
- }
- return true;
- }
-
- private void executeCommand(Integer messageId, Long userId, User user_from, String text, URI attachment) {
- try {
- CommandResult result = commandsManager.processCommand(user_from, text, attachment);
- if (result.getNewMessage().isPresent()) {
- com.juick.model.Message newMessage = result.getNewMessage().get();
- messagesService.setMessageProperty(newMessage.getMid(), newMessage.getRid(), "durovId",
- String.valueOf(messageId));
- }
- String messageTxt = StringUtils.isNotEmpty(result.getMarkdown()) ? result.getMarkdown()
- : "Unknown error or unsupported command";
- telegramNotify(userId, messageTxt, new com.juick.model.Message());
- } catch (Exception e) {
- logger.warn("telegram exception", e);
- }
- }
-
- private boolean isJuickLink(MessageEntity e) {
- return e.offset() == 0 && e.type().equals(MessageEntity.Type.text_link) && e.length() == 2;
- }
-
- public void telegramNotify(Long chatId, String msg, @Nonnull com.juick.model.Message source) {
- telegramNotify(chatId, msg, 0, source);
- }
-
- public void telegramNotify(Long chatId, String msg, Integer replyTo, @Nonnull com.juick.model.Message source) {
- String attachment = MessageUtils.attachmentUrl(source);
- boolean isSendTxt = true;
- if (!StringUtils.isEmpty(attachment)) {
- SendPhoto telegramPhoto = new SendPhoto(chatId, attachment);
- if (replyTo > 0) {
- telegramPhoto.replyToMessageId(replyTo);
- }
- telegramPhoto.parseMode(ParseMode.Markdown);
- if(msg.length() < 1024) {
- telegramPhoto.caption(msg);
- isSendTxt = false;
- }
- bot.execute(telegramPhoto, new Callback<>() {
- @Override
- public void onResponse(SendPhoto request, SendResponse response) {
- processTelegramResponse(chatId, response, source);
- }
-
- @Override
- public void onFailure(SendPhoto request, IOException e) {
- logger.warn("telegram failure", e);
- }
- });
- }
- if (isSendTxt){
- SendMessage telegramMessage = new SendMessage(chatId, msg);
- if (replyTo > 0) {
- telegramMessage.replyToMessageId(replyTo);
- }
- telegramMessage.parseMode(ParseMode.Markdown).disableWebPagePreview(true);
- bot.execute(telegramMessage, new Callback<>() {
- @Override
- public void onResponse(SendMessage request, SendResponse response) {
- processTelegramResponse(chatId, response, source);
- }
-
- @Override
- public void onFailure(SendMessage request, IOException e) {
- logger.warn("telegram failure", e);
- }
- });
- }
- }
-
- private void processTelegramResponse(Long chatId, SendResponse response, com.juick.model.Message source) {
- int userId = telegramService.getUser(chatId);
- if (!response.isOk()) {
- if (response.errorCode() == 403) {
- // remove from anonymous users
- telegramService.getAnonymous().stream().filter(c -> c.equals(chatId)).findFirst().ifPresent(
- d -> {
- telegramService.deleteAnonymous(d);
- logger.info("deleted {} chat", d);
- }
- );
- if (userId > 0) {
- User userToDelete = userService.getUserByUID(userId)
- .orElseThrow(IllegalStateException::new);
- boolean status = telegramService.deleteTelegramUser(userToDelete.getUid());
- logger.info("deleting telegram id of @{} : {}", userToDelete.getName(), status);
- }
- } else {
- logger.warn("error response, isOk: {}, errorCode: {}, description: {}",
- response.isOk(), response.errorCode(), response.description());
- }
- } else {
- if (MessageUtils.isReply(source)) {
- messagesService.setLastReadComment(userService.getUserByUID(userId)
- .orElseThrow(IllegalStateException::new), source.getMid(), source.getRid());
- User user = userService.getUserByUID(userId).orElseThrow(IllegalStateException::new);
- userService.updateLastSeen(user);
- applicationEventPublisher.publishEvent(
- new SystemEvent(this, SystemActivity.read(user, source)));
- }
- }
- }
-
- public void telegramSignupNotify(Long telegramId, String hash) {
- bot.execute(new SendMessage(telegramId,
- String.format("You are subscribed to all Juick messages. " +
- "[Create or link](http://juick.com/signup?type=durov&hash=%s) " +
- "an existing Juick account to get your subscriptions and ability to post messages", hash))
- .parseMode(ParseMode.Markdown), new Callback<>() {
- @Override
- public void onResponse(SendMessage request, SendResponse response) {
- logger.info("got response: {}", response.message());
- }
-
- @Override
- public void onFailure(SendMessage request, IOException e) {
- logger.warn("telegram failure", e);
- }
- });
- }
-
- @Override
- public void processSystemEvent(SystemEvent systemEvent) {
- var activity = systemEvent.getActivity();
- var type = activity.getType();
- if (type.equals(SystemActivity.ActivityType.message)) {
- processMessage(activity.getMessage(), activity.getTo());
- } else if (type.equals(SystemActivity.ActivityType.like)) {
- if (systemEvent.getActivity().getFrom().getName().equals(serviceUser)) {
- processTop(systemEvent.getActivity().getMessage());
- } else {
- processLike(activity.getFrom(), activity.getMessage(), activity.getTo());
- }
- } else if (type.equals(SystemActivity.ActivityType.follow)) {
- processFollow(activity.getFrom(), activity.getTo());
- }
- }
- private void processMessage(com.juick.model.Message jmsg, List<User> subscribedUsers) {
- if (jmsg.isService()) {
- return;
- }
- String msgUrl = formatUrl(jmsg);
- if (MessageUtils.isPM(jmsg)) {
- telegramService.getTelegramIdentifiers(Collections.singletonList(jmsg.getTo()))
- .forEach(c -> telegramNotify(c, formatPost(jmsg, true), jmsg));
- } else if (MessageUtils.isReply(jmsg)) {
- String fmsg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true));
- telegramService.getTelegramIdentifiers(
- subscribedUsers
- ).forEach(c -> telegramNotify(c, fmsg, jmsg));
- } else {
- String msg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true));
-
- List<Long> users = telegramService.getTelegramIdentifiers(subscribedUsers);
- List<Long> chats = telegramService.getAnonymous();
- // registered subscribed users
-
- users.forEach(c -> telegramNotify(c, msg, jmsg));
- // anonymous
- chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg, jmsg));
- }
- }
-
- private void processLike(User liker, com.juick.model.Message message, List<User> subscribers) {
- if (!liker.getName().equals(serviceUser)) {
- logger.info("Like received in tg listener");
- if (!userService.isInBLAny(message.getUser().getUid(), liker.getUid())) {
- telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser()))
- .forEach(c -> telegramNotify(c, String.format("%s recommends your [post](%s)",
- MessageUtils.getMarkdownUser(liker), formatUrl(message)), new com.juick.model.Message()));
- }
- telegramService.getTelegramIdentifiers(subscribers)
- .forEach(c -> telegramNotify(c, String.format("%s recommends you someone's [post](%s)",
- MessageUtils.getMarkdownUser(liker), formatUrl(message)), new com.juick.model.Message()));
- }
- }
-
- @Override
- public void processPingEvent(PingEvent pingEvent) {
-
- }
-
- private void processTop(com.juick.model.Message message) {
- telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser()))
- .forEach(c -> telegramNotify(c, String.format("Your [post](%s) became popular!",
- formatUrl(message)), new com.juick.model.Message()));
- }
-
- private void processFollow(User subscriber, List<User> target) {
- telegramService.getTelegramIdentifiers(target)
- .forEach(c -> telegramNotify(c, String.format("%s subscribed to your blog",
- MessageUtils.getMarkdownUser(subscriber)), new com.juick.model.Message()));
- }
-}
diff --git a/src/main/java/com/juick/server/TopManager.java b/src/main/java/com/juick/server/TopManager.java
deleted file mode 100644
index 15abb6cc..00000000
--- a/src/main/java/com/juick/server/TopManager.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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
- * 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.model.Message;
-import com.juick.model.Tag;
-import com.juick.model.User;
-import com.juick.www.api.SystemActivity;
-import com.juick.service.MessagesService;
-import com.juick.service.UserService;
-import com.juick.service.component.SystemEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-
-@Component
-public class TopManager {
- private static Logger logger = LoggerFactory.getLogger(TopManager.class);
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
-
- @Value("${service_user:juick}")
- private String serviceUsername;
-
- private User serviceUser;
-
- @PostConstruct
- public void init() {
- serviceUser = userService.getUserByName(serviceUsername);
- }
-
- @Scheduled(fixedRate = 3600000)
- public void updateTop() {
- messagesService.getPopularCandidates().forEach(m -> {
- Message jmsg = messagesService.getMessage(m).orElseThrow(IllegalStateException::new);
- logger.info("added {} to popular", m);
- messagesService.setMessagePopular(m, 1);
- List<String> tags = jmsg.getTags().stream().map(Tag::getName).map(String::toLowerCase).collect(Collectors.toList());
- if (!tags.contains("juick")) {
- applicationEventPublisher.publishEvent(new SystemEvent(this,
- SystemActivity.like(serviceUser, jmsg, Collections.emptyList())));
- }
- });
- }
-}
diff --git a/src/main/java/com/juick/server/TwitterManager.java b/src/main/java/com/juick/server/TwitterManager.java
deleted file mode 100644
index e424784c..00000000
--- a/src/main/java/com/juick/server/TwitterManager.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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
- * 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.model.Message;
-import com.juick.model.User;
-import com.juick.www.api.SystemActivity;
-import com.juick.service.UserService;
-import com.juick.service.component.*;
-import com.juick.service.CrosspostService;
-import com.juick.util.MessageUtils;
-import org.apache.commons.lang3.SerializationUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import twitter4j.TwitterFactory;
-import twitter4j.conf.ConfigurationBuilder;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-
-/**
- * @author Ugnich Anton
- */
-@Component
-public class TwitterManager implements NotificationListener {
-
- private static Logger logger = LoggerFactory.getLogger(TwitterManager.class);
-
- @Inject
- private CrosspostService crosspostService;
-
- @Value("${twitter_consumer_key:12345678}")
- private String twitter_consumer_key;
- @Value("${twitter_consumer_secret:secret}")
- private String twitter_consumer_secret;
- @Inject
- private UserService userService;
-
- @Value("${service_user:juick}")
- private String serviceUsername;
-
- private User serviceUser;
-
- @PostConstruct
- public void init() {
- serviceUser = userService.getUserByName(serviceUsername);
- }
-
- void twitterPost(final Message jmsg) {
- crosspostService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> {
- String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText());
- if (status.length() > 253) {
- status = status.substring(0, 252) + "…";
- }
- status += " http://juick.com/" + jmsg.getMid();
- ConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
- .setDebugEnabled(true)
- .setOAuthConsumerKey(twitter_consumer_key)
- .setOAuthConsumerSecret(twitter_consumer_secret);
- TwitterFactory tf = new TwitterFactory(configurationBuilder
- .setOAuthAccessToken(t.getToken())
- .setOAuthAccessTokenSecret(t.getSecret()).build());
- try {
- tf.getInstance().updateStatus(status);
- } catch (Exception e) {
- logger.info("Twitter exception", e);
- }
- });
- }
-
- @Override
- public void processSystemEvent(SystemEvent systemEvent) {
- var activity = systemEvent.getActivity();
- var msg = activity.getMessage();
- if (activity.getType().equals(SystemActivity.ActivityType.message)) {
- if (MessageUtils.isPM(msg) || MessageUtils.isReply(msg) || msg.isService()) {
- return;
- }
- if (StringUtils.isNotEmpty(crosspostService.getTwitterName(msg.getUser().getUid()))) {
- if (msg.getTags().stream().noneMatch(t -> t.getName().equals("notwitter"))) {
- twitterPost(msg);
- }
- }
- } else if (activity.getType().equals(SystemActivity.ActivityType.like)) {
- if (activity.getFrom().getName().equals(serviceUsername)) {
- processTop(msg);
- }
- }
- }
-
- @Override
- public void processPingEvent(PingEvent pingEvent) {
-
- }
-
- private void processTop(Message message) {
- Message jmsg = SerializationUtils.clone(message);
- jmsg.setUser(serviceUser);
- twitterPost(jmsg);
- }
-}
diff --git a/src/main/java/com/juick/server/Utils.java b/src/main/java/com/juick/server/Utils.java
deleted file mode 100644
index 58662c71..00000000
--- a/src/main/java/com/juick/server/Utils.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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
- * 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 javax.servlet.http.HttpServletRequest;
-import java.util.Optional;
-
-/**
- *
- * @author Ugnich Anton
- */
-public class Utils {
-
-
- public static String encodeSphinx(String str) {
- return str.replaceAll("@", "\\\\@")
- .replaceAll("\\'", "\\\\'")
- .replaceAll("=", "\\\\\\\\=");
- }
- /**
- * Returns the viewName to return for coming back to the sender url
- *
- * @param request Instance of {@link HttpServletRequest} or use an injected instance
- * @return Optional with the view name. Recomended to use an alternativa url with
- * {@link Optional#orElse(java.lang.Object)}
- */
- public static Optional<String> getPreviousPageByRequest(HttpServletRequest request)
- {
- return Optional.ofNullable(request.getHeader("Referer"));
- }
-}
diff --git a/src/main/java/com/juick/server/XMPPManager.java b/src/main/java/com/juick/server/XMPPManager.java
deleted file mode 100644
index 32f1b94e..00000000
--- a/src/main/java/com/juick/server/XMPPManager.java
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * 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
- * 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.model.User;
-import com.juick.formatters.PlainTextFormatter;
-import com.juick.model.CommandResult;
-import com.juick.www.api.SystemActivity;
-import com.juick.www.WebApp;
-import com.juick.server.xmpp.iq.MessageQuery;
-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;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.io.Resource;
-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.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;
-import rocks.xmpp.extensions.caps.EntityCapabilitiesManager;
-import rocks.xmpp.extensions.component.accept.ExternalComponent;
-import rocks.xmpp.extensions.disco.ServiceDiscoveryManager;
-import rocks.xmpp.extensions.disco.model.info.Identity;
-import rocks.xmpp.extensions.filetransfer.FileTransfer;
-import rocks.xmpp.extensions.filetransfer.FileTransferManager;
-import rocks.xmpp.extensions.nick.model.Nickname;
-import rocks.xmpp.extensions.oob.model.x.OobX;
-import rocks.xmpp.extensions.ping.PingManager;
-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 javax.annotation.Nonnull;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import java.io.IOException;
-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;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-
-/**
- * @author ugnich
- */
-public class XMPPManager implements NotificationListener {
-
- private static final Logger logger = LoggerFactory.getLogger("XMPP");
-
- private ExternalComponent xmpp;
- @Inject
- private CommandsManager commandsManager;
- @Value("${xmppbot_jid:juick@localhost}")
- private Jid jid;
- @Value("${hostname:localhost}")
- 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'}}")
- private String tmpDir;
- @Value("classpath:juick.png")
- private Resource vCardImage;
-
- @Inject
- private MessagesService messagesService;
- @Inject
- private UserService userService;
- @Inject
- private PMQueriesService pmQueriesService;
- @Inject
- private Executor applicationTaskExecutor;
- @Value("${service_user:juick}")
- private String serviceUsername;
- @Inject
- private WebApp webApp;
-
- private User serviceUser;
-
- @PostConstruct
- public void init() {
- logger.info("xmpp component start connecting to {}", componentPort);
- XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
- .extensions(Extension.of(com.juick.model.Message.class), Extension.of(MessageQuery.class))
- .debugger(LogbackDebugger.class)
- .defaultResponseTimeout(Duration.ofMillis(120000))
- .build();
- xmpp = ExternalComponent.create(componentName, password, configuration, componentHost, componentPort);
- ServiceDiscoveryManager serviceDiscoveryManager = xmpp.getManager(ServiceDiscoveryManager.class);
- serviceDiscoveryManager.addIdentity(Identity.clientBot().withName("Juick"));
- EntityCapabilitiesManager entityCapabilitiesManager = xmpp.getManager(EntityCapabilitiesManager.class);
- entityCapabilitiesManager.setNode("https://juick.com/caps");
- MessageDeliveryReceiptsManager messageDeliveryReceiptsManager = xmpp.getManager(MessageDeliveryReceiptsManager.class);
- messageDeliveryReceiptsManager.setEnabled(true);
- PingManager pingManager = xmpp.getManager(PingManager.class);
- pingManager.setEnabled(true);
- SoftwareVersionManager softwareVersionManager = xmpp.getManager(SoftwareVersionManager.class);
- softwareVersionManager.setSoftwareVersion(new SoftwareVersion("Juick", "2.x",
- System.getProperty("os.name", "generic")));
- VCard vCard = new VCard();
- vCard.setFormattedName("Juick");
- vCard.setBirthday(LocalDate.of(2008, 10, 22));
- try {
- vCard.setUrl(new URL("http://juick.com/"));
- vCard.setPhoto(new VCard.Image("image/png", IOUtils.toByteArray(vCardImage.getInputStream())));
- } catch (MalformedURLException e) {
- logger.error("invalid url", e);
- } catch (IOException e) {
- logger.warn("invalid resource", e);
- }
- xmpp.addIQHandler(MessageQuery.class, iq -> {
- Message warningMessage = new Message(iq.getFrom(), Message.Type.CHAT);
- warningMessage.setFrom(jid);
- warningMessage.setBody("Your XMPP client constantly polls us with XMPP query which is unsupported for years, please find http://juick.com/query#messages in your client code and remove that");
- xmpp.send(warningMessage);
- return iq.createError(new StanzaError(Condition.BAD_REQUEST, "Please stop this spam"));
- });
- xmpp.addIQHandler(VCard.class, new AbstractIQHandler(IQ.Type.GET) {
- @Override
- protected IQ processRequest(IQ iq) {
- if (iq.getTo().equals(jid) || iq.getTo().asBareJid().equals(jid.asBareJid())
- || iq.getTo().asBareJid().toEscapedString().equals(jid.getDomain())) {
- return iq.createResult(vCard);
- }
- User user = userService.getUserByName(iq.getTo().getLocal());
- if (!user.isAnonymous()) {
- User info = userService.getUserInfo(user);
- VCard userVCard = new VCard();
- userVCard.setFormattedName(info.getFullName());
- userVCard.setNickname(user.getName());
- try {
- userVCard.setPhoto(new VCard.Image(URI.create(webApp.getAvatarUrl(user))));
- if (info.getUrl() != null) {
- userVCard.setUrl(new URL(info.getUrl()));
- }
- } catch (MalformedURLException e) {
- logger.warn("url exception", e);
- }
- return iq.createResult(userVCard);
- }
- return iq.createError(Condition.BAD_REQUEST);
- }
- });
- xmpp.addInboundMessageListener(e -> {
- ClientMessage result = incomingMessage(e.getMessage());
- if (result != null) {
- xmpp.send(result);
- }
- });
- FileTransferManager fileTransferManager = xmpp.getManager(FileTransferManager.class);
- fileTransferManager.addFileTransferOfferListener(e -> {
- try {
- List<String> allowedTypes = new ArrayList<>() {{
- add("png");
- add("jpg");
- }};
- String attachmentExtension = FilenameUtils.getExtension(e.getName()).toLowerCase();
- String targetFilename = String.format("%s.%s",
- DigestUtils.md5Hex(String.format("%s-%s",
- e.getInitiator().toString(), e.getSessionId()).getBytes()), attachmentExtension);
- if (allowedTypes.contains(attachmentExtension)) {
- Path filePath = Paths.get(tmpDir, targetFilename);
- FileTransfer ft = e.accept(filePath).get();
- ft.addFileTransferStatusListener(st -> {
- logger.debug("{}: received {} of {}", e.getName(), st.getBytesTransferred(), e.getSize());
- if (st.getStatus().equals(FileTransfer.Status.COMPLETED)) {
- logger.info("transfer completed");
- try {
- Jid initiator = e.getInitiator();
- ClientMessage result = incomingMessageJuick(
- userService.getUserByJID(initiator.asBareJid().toEscapedString()), initiator,
- jid.getLocal(), StringUtils.defaultString(e.getDescription()).trim(), URI.create(String.format("juick://%s", targetFilename)));
- if (result != null) {
- xmpp.send(result);
- }
- } catch (Exception e1) {
- logger.error("ft error", e1);
- }
-
- } else if (st.getStatus().equals(FileTransfer.Status.FAILED)) {
- logger.info("transfer failed", ft.getException());
- Message msg = new Message();
- msg.setType(Message.Type.CHAT);
- msg.setFrom(jid);
- msg.setTo(e.getInitiator());
- msg.setBody("File transfer failed, please report to us");
- xmpp.sendMessage(msg);
- } else if (st.getStatus().equals(FileTransfer.Status.CANCELED)) {
- logger.info("transfer cancelled");
- }
- });
- ft.transfer();
- logger.info("transfer started");
- } else {
- e.reject();
- logger.info("transfer rejected");
- }
- } catch (IOException | InterruptedException | ExecutionException e1) {
- logger.error("ft error", e1);
- }
- });
- xmpp.addConnectionListener(event -> {
- if (event.getType().equals(rocks.xmpp.core.session.ConnectionEvent.Type.RECONNECTION_SUCCEEDED)) {
- logger.info("component connected");
- }
- });
- xmpp.addSessionStatusListener(event -> {
- logger.info("event: " + event.getStatus(), event.getThrowable());
- if (event.getStatus().equals(XmppSession.Status.AUTHENTICATED)) {
- logger.info("Authenticated, broadcasting...");
- broadcastPresence(null);
- }
- });
- xmpp.addInboundPresenceListener(event -> {
- incomingPresence(event.getPresence());
- });
- applicationTaskExecutor.execute(() -> {
- try {
- xmpp.connect();
- } catch (XmppException e) {
- logger.warn("xmpp exception", e);
- }
- });
- serviceUser = userService.getUserByName(serviceUsername);
- }
-
- private void sendJuickMessage(com.juick.model.Message jmsg, List<User> users) {
- List<String> jids = new ArrayList<>();
-
- for (User user : users) {
- jids.addAll(userService.getJIDsbyUID(user.getUid()));
- }
- com.juick.model.Message fullMsg = messagesService.getMessage(jmsg.getMid()).orElseThrow(IllegalStateException::new);
- String txt = "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(fullMsg) + "\n";
- String attachmentUrl = MessageUtils.attachmentUrl(fullMsg);
- if (StringUtils.isNotEmpty(attachmentUrl)) {
- txt += attachmentUrl + "\n";
- }
- txt += StringUtils.defaultString(jmsg.getText()) + "\n\n";
- txt += "#" + jmsg.getMid() + " http://juick.com/m/" + jmsg.getMid();
-
- Nickname nick = new Nickname("@" + jmsg.getUser().getName());
-
- Message msg = new Message();
- msg.setFrom(jid);
- msg.setBody(txt);
- msg.setType(Message.Type.CHAT);
- msg.setThread("juick-" + jmsg.getMid());
- msg.addExtension(jmsg);
- msg.addExtension(nick);
- if (StringUtils.isNotEmpty(attachmentUrl)) {
- try {
- OobX oob = new OobX(new URI(attachmentUrl));
- msg.addExtension(oob);
- } catch (URISyntaxException e) {
- logger.warn("uri exception", e);
- }
- }
- for (String jid : jids) {
- msg.setTo(Jid.of(jid));
- xmpp.send(ClientMessage.from(msg));
- }
- }
-
- private void sendJuickComment(com.juick.model.Message jmsg, List<User> users) {
- String replyQuote;
- String replyTo;
-
- com.juick.model.Message replyMessage = jmsg.getReplyto() > 0 ? messagesService.getReply(jmsg.getMid(), jmsg.getReplyto())
- : messagesService.getMessage(jmsg.getMid()).orElseThrow(IllegalStateException::new);
- replyTo = replyMessage.getUser().getName();
- com.juick.model.Message fullReply = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
- replyQuote = StringUtils.defaultString(fullReply.getReplyQuote());
-
- String txt = "Reply by @" + jmsg.getUser().getName() + ":\n" + replyQuote + "\n@" + replyTo + " ";
- String attachmentUrl = MessageUtils.attachmentUrl(fullReply);
- if (StringUtils.isNotEmpty(attachmentUrl)) {
- txt += attachmentUrl + "\n";
- }
- txt += StringUtils.defaultString(jmsg.getText()) + "\n\n" + "#" + jmsg.getMid() + "/" + jmsg.getRid() + " http://juick.com/m/" + jmsg.getMid() + "#" + jmsg.getRid();
-
- Message msg = new Message();
- msg.setFrom(jid);
- msg.setBody(txt);
- msg.setType(Message.Type.CHAT);
- msg.addExtension(jmsg);
- for (User user : users) {
- for (String jid : userService.getJIDsbyUID(user.getUid())) {
- msg.setTo(Jid.of(jid));
- xmpp.send(ClientMessage.from(msg));
- }
- }
- }
-
- @Override
- public void processSystemEvent(SystemEvent systemEvent) {
- var activity = systemEvent.getActivity();
- var type = activity.getType();
- if (type.equals(SystemActivity.ActivityType.message)) {
- processMessage(activity.getMessage(), activity.getTo());
- } else if (type.equals(SystemActivity.ActivityType.like)) {
- if (systemEvent.getActivity().getFrom().equals(serviceUser)) {
- processTop(systemEvent.getActivity().getMessage());
- } else {
- processLike(activity.getFrom(), activity.getMessage(), activity.getTo());
- }
- }
- }
- private void processMessage(com.juick.model.Message msg, List<User> subscribers) {
- if (msg.isService()) {
- return;
- }
- if (MessageUtils.isPM(msg)) {
- userService.getJIDsbyUID(msg.getTo().getUid())
- .forEach(userJid -> {
- Message mm = new Message();
- mm.setTo(Jid.of(userJid));
- mm.setType(Message.Type.CHAT);
- boolean inroster = pmQueriesService.havePMinRoster(msg.getUser().getUid(), userJid);
- if (inroster) {
- mm.setFrom(Jid.of(msg.getUser().getName(), "juick.com", "Juick"));
- mm.setBody(msg.getText());
- } else {
- mm.setFrom(jid);
- mm.setBody("Private message from @" + msg.getUser().getName() + ":\n" + msg.getText());
- }
- xmpp.send(ClientMessage.from(mm));
- });
- } else if (MessageUtils.isReply(msg)) {
- sendJuickComment(msg, subscribers);
- }
- else {
- sendJuickMessage(msg, subscribers);
- }
- }
-
- private ClientMessage makeReply(Jid jidTo, String txt) {
- Message reply = new Message();
- reply.setFrom(jid);
- reply.setTo(jidTo);
- reply.setType(Message.Type.CHAT);
- reply.setBody(txt);
- return ClientMessage.from(reply);
- }
-
- public void processLike(User liker, com.juick.model.Message jmsg, List<User> users) {
- if (!userService.isInBLAny(jmsg.getUser().getUid(), liker.getUid())) {
- userService.getJIDsbyUID(jmsg.getUser().getUid()).forEach(authorJid -> {
- Message xmppMessage = new Message();
- xmppMessage.setFrom(jid);
- xmppMessage.setTo(Jid.of(authorJid));
- xmppMessage.setType(Message.Type.CHAT);
- xmppMessage.addExtension(jmsg);
- xmppMessage.setBody(String.format("%s recommended your post #%d. %s",
- liker.getName(), jmsg.getMid(), PlainTextFormatter.formatUrl(jmsg)));
- xmpp.send(ClientMessage.from(xmppMessage));
- });
- }
-
- String txt = "Recommended by @" + liker.getName() + ":\n";
- txt += "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(jmsg) + "\n";
- String attachmentUrl = MessageUtils.attachmentUrl(jmsg);
- if (StringUtils.isNotEmpty(attachmentUrl)) {
- txt += attachmentUrl + "\n";
- }
- txt += StringUtils.defaultString(jmsg.getText()) + "\n\n";
- txt += "#" + jmsg.getMid();
- if (jmsg.getReplies() > 0) {
- if (jmsg.getReplies() % 10 == 1 && jmsg.getReplies() % 100 != 11) {
- txt += " (" + jmsg.getReplies() + " reply)";
- } else {
- txt += " (" + jmsg.getReplies() + " replies)";
- }
- }
- txt += " http://juick.com/m/" + jmsg.getMid();
-
- Nickname nick = new Nickname("@" + jmsg.getUser().getName());
-
- Message msg = new Message();
- msg.setFrom(jid);
- msg.setBody(txt);
- msg.setType(Message.Type.CHAT);
- msg.setThread("juick-" + jmsg.getMid());
- msg.addExtension(jmsg);
- msg.addExtension(nick);
- if (StringUtils.isNotEmpty(attachmentUrl)) {
- try {
- OobX oob = new OobX(new URI(attachmentUrl));
- msg.addExtension(oob);
- } catch (URISyntaxException e) {
- logger.warn("uri exception", e);
- }
- }
-
- for (User user : users) {
- for (String jid : userService.getJIDsbyUID(user.getUid())) {
- msg.setTo(Jid.of(jid));
- xmpp.send(ClientMessage.from(msg));
- }
- }
- }
-
- @Override
- public void processPingEvent(PingEvent pingEvent) {
- userService.getJIDsbyUID(pingEvent.getPinger().getUid())
- .forEach(userJid -> {
- Presence p = new Presence(Jid.of(userJid));
- p.setFrom(jid);
- p.setPriority((byte) 10);
- xmpp.send(ClientPresence.from(p));
- });
- }
-
- public void processTop(com.juick.model.Message message) {
- try {
- commandsManager.processCommand(serviceUser, String.format("! #%d", message.getMid()), URI.create(StringUtils.EMPTY));
- } catch (Exception e) {
- logger.warn("XMPP error", e);
- }
- }
-
- private void incomingPresence(Presence p) {
- final String username = p.getTo().getLocal();
- final boolean toJuick = username.equals(jid.getLocal());
-
- if (p.getType() == null) {
- Presence reply = new Presence();
- reply.setFrom(p.getTo().asBareJid());
- reply.setTo(p.getFrom().asBareJid());
- reply.setType(Presence.Type.UNSUBSCRIBE);
- xmpp.send(ClientPresence.from(reply));
- } else if (p.getType().equals(Presence.Type.PROBE)) {
- int uid_to = 0;
- if (!toJuick) {
- uid_to = userService.getUIDbyName(username);
- } else {
- User visitor = userService.getUserByJID(p.getFrom().asBareJid().toEscapedString());
- if (visitor != null) {
- userService.updateLastSeen(visitor);
- }
- }
-
- if (toJuick || uid_to > 0) {
- Presence reply = new Presence();
- reply.setFrom(p.getTo().withResource(jid.getResource()));
- reply.setTo(p.getFrom());
- reply.setPriority((byte)10);
- if (!userService.getActiveJIDs().contains(p.getFrom().asBareJid().toEscapedString())) {
- reply.setStatus("Send ON to enable notifications");
- }
- xmpp.send(ClientPresence.from(reply));
- } else {
- Presence reply = new Presence();
- reply.setFrom(p.getTo());
- reply.setTo(p.getFrom());
- reply.setType(Presence.Type.ERROR);
- reply.setId(p.getId());
- reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND));
- xmpp.send(ClientPresence.from(reply));
- }
- } else if (p.getType().equals(Presence.Type.SUBSCRIBE)) {
- boolean canSubscribe = false;
- if (toJuick) {
- canSubscribe = true;
- } else {
- int uid_to = userService.getUIDbyName(username);
- if (uid_to > 0) {
- pmQueriesService.addPMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString());
- canSubscribe = true;
- }
- }
- if (canSubscribe) {
- Presence reply = new Presence();
- reply.setFrom(p.getTo());
- reply.setTo(p.getFrom());
- reply.setType(Presence.Type.SUBSCRIBED);
- xmpp.send(ClientPresence.from(reply));
-
- reply.setFrom(reply.getFrom().withResource(jid.getResource()));
- reply.setPriority((byte) 10);
- reply.setType(null);
- xmpp.send(ClientPresence.from(reply));
- } else {
- Presence reply = new Presence();
- reply.setFrom(p.getTo());
- reply.setTo(p.getFrom());
- reply.setType(Presence.Type.ERROR);
- reply.setId(p.getId());
- reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND));
- xmpp.send(ClientPresence.from(reply));
- }
- } else if (p.getType().equals(Presence.Type.UNSUBSCRIBE)) {
- if (!toJuick) {
- int uid_to = userService.getUIDbyName(username);
- if (uid_to > 0) {
- pmQueriesService.removePMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString());
- }
- }
-
- Presence reply = new Presence();
- reply.setFrom(p.getTo());
- reply.setTo(p.getFrom());
- reply.setType(Presence.Type.UNSUBSCRIBED);
- xmpp.send(ClientPresence.from(reply));
- }
- }
-
- public ClientMessage incomingMessage(Message msg) {
- if (msg.getType() != null && msg.getType().equals(Message.Type.ERROR)) {
- StanzaError error = msg.getError();
- if (error != null && error.getCondition().equals(Condition.RESOURCE_CONSTRAINT)) {
- // offline query is full, deactivating this jid
- if (userService.setActiveStatusForJID(msg.getFrom().toEscapedString(), UserService.ActiveStatus.Inactive)) {
- logger.info("{} is inactive now", msg.getFrom());
- return null;
- }
- }
- return null;
- }
- Jid to = msg.getTo();
- if (to.getDomain().equals(xmpp.getDomain().toEscapedString()) || to.equals(this.jid)) {
- User user_from = userService.getUserByJID(msg.getFrom().asBareJid().toEscapedString());
- if ((user_from == null || user_from.isAnonymous()) && !msg.getFrom().equals(jid)) {
- String signuphash = userService.getSignUpHashByJID(msg.getFrom().asBareJid().toEscapedString());
- return makeReply(msg.getFrom(), "Для того, чтобы начать пользоваться сервисом, пожалуйста пройдите быструю регистрацию: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nTo start using Juick, please sign up: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nIf you already have an account on Juick, you will be proposed to attach this JabberID to your existing account.");
- }
- URI attachment = URI.create(StringUtils.EMPTY);
- OobX oobX = msg.getExtension(OobX.class);
- if (oobX != null) {
- attachment = oobX.getUri();
- }
- try {
- return incomingMessageJuick(user_from, msg.getFrom(), msg.getTo().getLocal(), StringUtils.defaultString(msg.getBody()).trim(), attachment);
- } catch (Exception e1) {
- logger.warn("message exception", e1);
- }
- }
- 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()) {
- return null;
- }
-
- messagesService.getUnread(user_from).forEach(mid -> messagesService.setRead(user_from, mid));
-
- int commandlen = command.length();
-
- // COMPATIBILITY
- if (commandlen > 7 && command.substring(0, 3).equalsIgnoreCase("PM ")) {
- command = command.substring(3);
- }
-
- if (!jid.getLocal().equals(to)) {
- // PM
- if (!StringUtils.isEmpty(command)) {
- commandsManager.commandPM(user_from, null, to, command);
- return null;
- }
- }
-
- try {
- CommandResult result = commandsManager.processCommand(user_from, command, attachment);
- if (StringUtils.isNotBlank(result.getText())) {
- return makeReply(from, result.getText());
- }
- } catch (Exception e) {
- logger.warn("xmpp command exception", e);
- return makeReply(from, "Error processing command");
- }
- return null;
- }
-
- private void broadcastPresence(Presence.Type type) {
- Presence presence = new Presence();
- presence.setFrom(jid);
- if (type != null) {
- presence.setType(type);
- }
- userService.getActiveJIDs().forEach(j -> {
- try {
- presence.setTo(Jid.of(j));
- xmpp.send(ClientPresence.from(presence));
- } catch (IllegalArgumentException ex) {
- logger.warn("Invalid jid: {}", j, ex);
- }
- });
- }
-
- @PreDestroy
- public void close() throws Exception {
- broadcastPresence(Presence.Type.UNAVAILABLE);
- if (xmpp != null) {
- xmpp.close();
- }
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java b/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java
deleted file mode 100644
index 56edffa7..00000000
--- a/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.www.api.activity.model.Activity;
-import com.juick.server.helpers.HeaderRequestInterceptor;
-import org.apache.http.client.config.CookieSpecs;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.protocol.HttpContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.web.client.RestTemplate;
-
-import javax.inject.Inject;
-import java.net.URI;
-import java.util.Collections;
-
-@Configuration
-public class ActivityPubClientConfig {
- @Inject
- ActivityPubClientErrorHandler activityPubClientErrorHandler;
- @Inject
- ObjectMapper jsonMapper;
- @Bean
- public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
- MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
- converter.setObjectMapper(jsonMapper);
- return converter;
- }
- @Bean
- public RestTemplate apClient() {
- RestTemplate restTemplate = new RestTemplate();
- restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory() {
- @Override
- protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
- HttpClientContext context = HttpClientContext.create();
- context.setRequestConfig(getRequestConfig());
- return context;
- }
- RequestConfig getRequestConfig() {
- RequestConfig.Builder builder = RequestConfig.custom()
- .setCookieSpec(CookieSpecs.IGNORE_COOKIES);
- return builder.build();
- }
- });
- restTemplate.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
- 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/ActivityPubClientErrorHandler.java b/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java
deleted file mode 100644
index edabadd7..00000000
--- a/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.juick.service.activities.DeleteUserEvent;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.DefaultResponseErrorHandler;
-
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-
-@Component
-public class ActivityPubClientErrorHandler extends DefaultResponseErrorHandler {
- private static final Logger logger = LoggerFactory.getLogger("ActivityPub");
-
- @Inject
- private ApplicationEventPublisher applicationEventPublisher;
- @Override
- public void handleError(URI contextUri, HttpMethod method, @Nonnull ClientHttpResponse response) throws IOException {
- logger.warn("HTTP ERROR {} {} : {}", response.getStatusCode().value(),
- response.getStatusText(), IOUtils.toString(response.getBody(), StandardCharsets.UTF_8));
- if (response.getStatusCode().equals(HttpStatus.GONE)) {
- logger.warn("Server report {} is gone, deleting", contextUri.toASCIIString());
- applicationEventPublisher.publishEvent(new DeleteUserEvent(this, contextUri.toASCIIString()));
- }
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java b/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
deleted file mode 100644
index 75d247bf..00000000
--- a/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.juick.www.rss.MessagesView;
-import com.juick.www.rss.RepliesView;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-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;
-
-/**
- * Created by aalexeev on 11/12/16.
- */
-@Configuration
-@EnableAsync(proxyTargetClass = true)
-@EnableScheduling
-public class ApiAppConfiguration implements WebMvcConfigurer {
- @Bean
- public BeanNameViewResolver beanNameViewResolver() {
- return new BeanNameViewResolver();
- }
- @Bean
- AbstractRssFeedView messagesView() {
- return new MessagesView();
- }
- @Bean
- AbstractRssFeedView repliesView() {
- return new RepliesView();
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java b/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java
deleted file mode 100644
index e84c0c40..00000000
--- a/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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
- * 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.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.core.io.Resource;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
-
-/**
- * Created by vitalyster on 28.06.2016.
- */
-@Configuration
-public class BaseWebConfiguration implements WebMvcConfigurer {
-
- @Value("${keystore:classpath:juick-test-key.p12}")
- private Resource keystore;
- @Value("${keystore_password:secret}")
- private String keystorePassword;
-
- @Bean
- public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
- return new ResourceUrlEncodingFilter();
- }
-
- @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/MailConfiguration.java b/src/main/java/com/juick/server/configuration/MailConfiguration.java
deleted file mode 100644
index 31034339..00000000
--- a/src/main/java/com/juick/server/configuration/MailConfiguration.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2008-2020, 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.configuration;
-
-import com.juick.server.EmailManager;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@ConditionalOnProperty("service_email")
-public class MailConfiguration {
- @Bean
- public EmailManager emailManager() {
- return new EmailManager();
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/SapeConfiguration.java b/src/main/java/com/juick/server/configuration/SapeConfiguration.java
deleted file mode 100644
index 8892115d..00000000
--- a/src/main/java/com/juick/server/configuration/SapeConfiguration.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-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 ru.sape.Sape;
-
-/**
- * Created by vitalyster on 29.03.2017.
- */
-@Configuration
-@ConditionalOnProperty("sape_user")
-public class SapeConfiguration {
- @Value("${sape_user:}")
- private String token;
-
- @Bean
- public Sape sape() {
- return new Sape(token, "juick.com", 2000, 3600);
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/SecurityConfig.java b/src/main/java/com/juick/server/configuration/SecurityConfig.java
deleted file mode 100644
index 0fab087f..00000000
--- a/src/main/java/com/juick/server/configuration/SecurityConfig.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2008-2020, 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.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.entities.JuickUser;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
-import org.springframework.http.HttpMethod;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.builders.WebSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.config.http.SessionCreationPolicy;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.authentication.NullRememberMeServices;
-import org.springframework.security.web.authentication.RememberMeServices;
-import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
-import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
-import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.CorsConfigurationSource;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Created by aalexeev on 11/21/16.
- */
-@EnableWebSecurity
-public class SecurityConfig {
- @Resource
- private UserService userService;
-
- private static final String COOKIE_NAME = "juick-remember-me";
-
- @Bean
- 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)
- public static class ApiConfig extends WebSecurityConfigurerAdapter {
- @Value("${auth_remember_me_key:secret}")
- private String rememberMeKey;
- @Resource
- private UserService userService;
- @Resource
- private SignatureManager signatureManager;
- ApiConfig() {
- super(true);
- }
- @Bean
- RememberMeServices apiTokenServices() {
- return new NullRememberMeServices();
- }
- @Bean
- public HashParamAuthenticationFilter apiAuthenticationFilter() {
- return new HashParamAuthenticationFilter(userService, apiTokenServices());
- }
-
- @Override
- 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/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/_applelogin", "/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)
- .and()
- .httpBasic().authenticationEntryPoint(juickAuthenticationEntryPoint())
- .and().cors().configurationSource(corsConfigurationSource())
- .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and().exceptionHandling().authenticationEntryPoint(juickAuthenticationEntryPoint())
- .and()
- .rememberMe()
- .alwaysRemember(true)
- .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30))
- .rememberMeServices(apiTokenServices())
- .key(rememberMeKey)
- .and()
- .headers().defaultsDisabled().cacheControl();
- }
-
- @Bean
- public AuthenticationEntryPoint juickAuthenticationEntryPoint() {
- var entryPoint = new BasicAuthenticationEntryPoint();
- entryPoint.setRealmName("Juick");
- return entryPoint;
- }
-
- @Override
- public void configure(WebSecurity web) {
- web.debug(false);
- web.ignoring().antMatchers("/api/v2/api-docs", "/api/configuration/ui", "/api/swagger-resources/**",
- "/api/configuration/**", "/swagger-ui.html", "/webjars/**", "/h2-console/**");
- }
- }
-
- @Configuration
- public static class WebConfig extends WebSecurityConfigurerAdapter {
- @Value("${auth_remember_me_key:secret}")
- private String rememberMeKey;
- @Value("${web_domain:localhost}")
- private String webDomain;
- @Resource
- private UserService userService;
- @Inject
- private UserDetailsService userDetailsService;
- @Bean
- @Qualifier("www")
- public HashParamAuthenticationFilter wwwAuthenticationFilter() {
- return new HashParamAuthenticationFilter(userService, hashCookieServices());
- }
- @Bean
- @Qualifier("www")
- public RememberMeServices hashCookieServices() {
- TokenBasedRememberMeServices services = new TokenBasedRememberMeServices(
- rememberMeKey, userDetailsService);
-
- services.setCookieName(COOKIE_NAME);
- services.setCookieDomain(webDomain);
- services.setAlwaysRemember(true);
- services.setTokenValiditySeconds(6 * 30 * 24 * 3600);
- services.setUseSecureCookie(false); // TODO set true if https is supports
-
- return services;
- }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class)
- .authorizeRequests()
- .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment")
- .authenticated()
- .antMatchers("/actuator/**").hasRole("ADMIN")
- .anyRequest().permitAll()
- .and()
- .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY)
- .and().cors().configurationSource(corsConfigurationSource())
- .and().sessionManagement()
- .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .invalidSessionUrl("/")
- .and()
- .logout()
- .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
- .invalidateHttpSession(true)
- .logoutUrl("/logout")
- .logoutSuccessUrl("/")
- .deleteCookies("hash", COOKIE_NAME)
- .and()
- .formLogin()
- .loginPage("/login")
- .permitAll()
- .defaultSuccessUrl("/")
- .loginProcessingUrl("/login")
- .usernameParameter("username")
- .passwordParameter("password")
- .failureUrl("/login?error=1")
- .and()
- .rememberMe()
- .rememberMeCookieDomain(webDomain).key(rememberMeKey)
- .rememberMeServices(hashCookieServices())
- .and()
- .csrf().disable()
- .headers().defaultsDisabled().cacheControl();
- }
- @Override
- public void configure(WebSecurity web) {
- web.debug(false);
- web.ignoring().antMatchers("/style*.css", "/scripts*.js", "/h2-console/**", "/.well-known/**");
- }
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/SignInWithAppleConfig.java b/src/main/java/com/juick/server/configuration/SignInWithAppleConfig.java
deleted file mode 100644
index 310c5899..00000000
--- a/src/main/java/com/juick/server/configuration/SignInWithAppleConfig.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.github.scribejava.apis.AppleClientSecretGenerator;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.io.Resource;
-
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
-
-@Configuration
-public class SignInWithAppleConfig {
- @Value("${apple_app_id:com.example.app}")
- private String appId;
- @Value("${apple_team_id:teamid}")
- private String teamId;
- @Value("${apple_key_id:keyid}")
- private String keyId;
- @Value("${apple_key_path:classpath:testkey.p8}")
- private Resource keyPath;
-
- @Bean
- public AppleClientSecretGenerator clientSecretGenerator() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
- return new AppleClientSecretGenerator(appId, teamId, keyId, keyPath.getFile().toPath());
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/StorageConfiguration.java b/src/main/java/com/juick/server/configuration/StorageConfiguration.java
deleted file mode 100644
index f4a80ece..00000000
--- a/src/main/java/com/juick/server/configuration/StorageConfiguration.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.juick.service.ImagesService;
-import com.juick.service.ImagesServiceImpl;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class StorageConfiguration {
-
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
- @Bean
- public ImagesService imagesService() {
- return new ImagesServiceImpl(imgDir, tmpDir);
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/TelegramConfig.java b/src/main/java/com/juick/server/configuration/TelegramConfig.java
deleted file mode 100644
index c56d7d0e..00000000
--- a/src/main/java/com/juick/server/configuration/TelegramConfig.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.juick.server.TelegramBotManager;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@ConditionalOnProperty(name = "telegram_token")
-public class TelegramConfig {
- @Bean
- public TelegramBotManager telegramBotManager() {
- return new TelegramBotManager();
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java b/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java
deleted file mode 100644
index 8e874e43..00000000
--- a/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.juick.service.HelpService;
-import com.juick.service.TagService;
-import com.juick.service.UserService;
-import com.mitchellbosecke.pebble.PebbleEngine;
-import com.mitchellbosecke.pebble.extension.FormatterExtension;
-import com.mitchellbosecke.pebble.loader.ClasspathLoader;
-import com.mitchellbosecke.pebble.loader.Loader;
-import com.mitchellbosecke.pebble.spring.extension.SpringExtension;
-import com.mitchellbosecke.pebble.spring.servlet.PebbleViewResolver;
-import org.apache.commons.codec.CharEncoding;
-import org.commonmark.ext.autolink.AutolinkExtension;
-import org.commonmark.node.Link;
-import org.commonmark.parser.Parser;
-import org.commonmark.renderer.html.HtmlRenderer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.cache.annotation.EnableCaching;
-import org.springframework.cache.caffeine.CaffeineCacheManager;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.CacheControl;
-import org.springframework.web.servlet.ViewResolver;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import org.springframework.web.servlet.resource.VersionResourceResolver;
-
-import javax.inject.Inject;
-import java.net.MalformedURLException;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Created by aalexeev on 11/22/16.
- */
-@Configuration
-@EnableCaching
-public class WwwAppConfiguration implements WebMvcConfigurer {
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
- @Bean
- public CaffeineCacheManager cacheManager() {
- return new CaffeineCacheManager("help");
- }
-
- @Bean
- public HelpService helpService() {
- return new HelpService("help");
- }
-
- @Bean
- public Parser cmParser() {
- return Parser.builder().extensions(Collections.singletonList(AutolinkExtension.create())).build();
- }
- @Bean
- public HtmlRenderer helpRenderer() {
- return HtmlRenderer.builder()
- .attributeProviderFactory(context -> (node, tagName, attributes) -> {
- if (node instanceof Link) {
- Link link = (Link) node;
- if (link.getDestination().startsWith("/")) {
- String destination = "/" + helpService().getHelpPath() + link.getDestination();
- link.setDestination(destination);
- attributes.put("href", destination);
- }
- }
- })
- .build();
- }
- @Bean
- public Loader templateLoader() {
- return new ClasspathLoader();
- }
-
- @Bean
- public SpringExtension springExtension() {
- return new SpringExtension();
- }
-
- @Bean
- public PebbleEngine pebbleEngine() {
- boolean devToolsArePresent = false;
- try {
- Class.forName("org.springframework.boot.devtools.livereload.Connection");
- devToolsArePresent = true;
- } catch (ClassNotFoundException e) {
- // release mode
- }
- return new PebbleEngine.Builder()
- .loader(this.templateLoader())
- .cacheActive(!devToolsArePresent)
- .extension(springExtension())
- .extension(new FormatterExtension())
- .newLineTrimming(false)
- .strictVariables(true)
- .build();
- }
-
- @Bean
- public ViewResolver viewResolver() {
- PebbleViewResolver viewResolver = new PebbleViewResolver();
- viewResolver.setPrefix("templates");
- viewResolver.setSuffix(".html");
- viewResolver.setPebbleEngine(pebbleEngine());
- viewResolver.setCharacterEncoding(CharEncoding.UTF_8);
- return viewResolver;
- }
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- try {
- registry
- .addResourceHandler("/**", "/i/a/**")
- .addResourceLocations("classpath:/static/", Paths.get(imgDir, "/a/").toUri().toURL().toString())
- .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
- .resourceChain(false)
- .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**", "/i/a/**"));
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/src/main/java/com/juick/server/configuration/XMPPConfig.java b/src/main/java/com/juick/server/configuration/XMPPConfig.java
deleted file mode 100644
index 62e19c71..00000000
--- a/src/main/java/com/juick/server/configuration/XMPPConfig.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
- * 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.configuration;
-
-import com.juick.server.XMPPManager;
-import com.juick.server.xmpp.JidConverter;
-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.core.convert.ConversionService;
-import org.springframework.format.support.DefaultFormattingConversionService;
-
-@Configuration
-@ConditionalOnProperty("xmppbot_jid")
-public class XMPPConfig {
- @Bean
- public static ConversionService conversionService() {
- DefaultFormattingConversionService cs = new DefaultFormattingConversionService();
- cs.addConverter(new JidConverter());
- return cs;
- }
- @Bean
- 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
deleted file mode 100644
index 8fb21ac5..00000000
--- a/src/main/java/com/juick/server/helpers/HeaderRequestInterceptor.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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
- * 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.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/helpers/annotation/UserCommand.java b/src/main/java/com/juick/server/helpers/annotation/UserCommand.java
deleted file mode 100644
index d25810d2..00000000
--- a/src/main/java/com/juick/server/helpers/annotation/UserCommand.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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
- * 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.helpers.annotation;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Created by oxpa on 22.03.16.
- */
-@Target({ElementType.TYPE, ElementType.METHOD})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface UserCommand {
- /**
- *
- * @return a command pattern
- */
- String pattern() default StringUtils.EMPTY;
-
- /**
- *
- * @return pattern flags
- */
- int patternFlags() default 0;
-
- /**
- *
- * @return a string used in HELP command output. Basically, only 1 string
- */
- String help() default StringUtils.EMPTY;
-}
diff --git a/src/main/java/com/juick/server/util/HttpBadRequestException.java b/src/main/java/com/juick/server/util/HttpBadRequestException.java
deleted file mode 100644
index 6dfc165e..00000000
--- a/src/main/java/com/juick/server/util/HttpBadRequestException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-/**
- * Created by vt on 24/11/2016.
- */
-@ResponseStatus(value = HttpStatus.BAD_REQUEST)
-public class HttpBadRequestException extends RuntimeException {
- public HttpBadRequestException() {
- super("the request was bad", null, false, false);
- }
-}
diff --git a/src/main/java/com/juick/server/util/HttpForbiddenException.java b/src/main/java/com/juick/server/util/HttpForbiddenException.java
deleted file mode 100644
index 0247f531..00000000
--- a/src/main/java/com/juick/server/util/HttpForbiddenException.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-/**
- * Created by vt on 24/11/2016.
- */
-@ResponseStatus(value = HttpStatus.FORBIDDEN)
-public class HttpForbiddenException extends RuntimeException {
- public HttpForbiddenException() {
- super(StringUtils.EMPTY, null, false, false);
- }
-
-}
diff --git a/src/main/java/com/juick/server/util/HttpNotFoundException.java b/src/main/java/com/juick/server/util/HttpNotFoundException.java
deleted file mode 100644
index dd5a2e1b..00000000
--- a/src/main/java/com/juick/server/util/HttpNotFoundException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-/**
- * Created by vt on 24/11/2016.
- */
-@ResponseStatus(value = HttpStatus.NOT_FOUND)
-public class HttpNotFoundException extends RuntimeException {
- public HttpNotFoundException() {
- super(StringUtils.EMPTY, null, false, false);
- }
-}
diff --git a/src/main/java/com/juick/server/util/HttpUtils.java b/src/main/java/com/juick/server/util/HttpUtils.java
deleted file mode 100644
index beef5d60..00000000
--- a/src/main/java/com/juick/server/util/HttpUtils.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.MediaType;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReader;
-import javax.imageio.stream.ImageInputStream;
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Iterator;
-import java.util.UUID;
-
-/**
- *
- * @author Ugnich Anton
- */
-public class HttpUtils {
- private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
-
- public static URI receiveMultiPartFile(MultipartFile attach, String tmpDir) throws IOException {
- if (attach != null && !attach.isEmpty()) {
- ImageInputStream iis = ImageIO.createImageInputStream(attach.getInputStream());
- Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
-
- String format = StringUtils.EMPTY;
- while (readers.hasNext()) {
- ImageReader read = readers.next();
- format = read.getFormatName();
- }
- String attachmentType = attachmentTypeFromFormat(format);
- if (attachmentType.equals("jpg") || attachmentType.equals("png")) {
- String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType;
- try {
- Files.write(Paths.get(tmpDir, attachmentFName),
- attach.getBytes());
- return URI.create(String.format("juick://%s", attachmentFName));
- } catch (IOException e) {
- logger.warn("file receive error", e);
- }
- }
- logger.warn("file type is unknown: {}", attach.getOriginalFilename());
- }
- return URI.create(StringUtils.EMPTY);
- }
-
- private static String attachmentTypeFromFormat(String format) throws IOException {
- if (format != null && format.equals("JPEG")) {
- return "jpg";
- } else if (format != null && format.equals("png")) {
- return "png";
- } else {
- throw new IOException("Wrong file type: " + format);
- }
- }
-
- public static String mediaType(String attachmentType) {
- return attachmentType.equals("jpg") ? MediaType.IMAGE_JPEG_VALUE : MediaType.IMAGE_PNG_VALUE;
- }
-
- public static URI downloadImage(URL url, String tmpDir) throws IOException {
- ImageInputStream iis = ImageIO.createImageInputStream(url.openStream());
- Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
-
- String format = StringUtils.EMPTY;
- while (readers.hasNext()) {
- ImageReader read = readers.next();
- format = read.getFormatName();
- }
- URLConnection urlConn;
- try {
- urlConn = url.openConnection();
- } catch (IOException e) {
- logger.error(String.format("Failed open url: %s", url.toString()));
- throw e;
- }
-
- try (InputStream is = new BufferedInputStream(urlConn.getInputStream())) {
- String attachmentType = attachmentTypeFromFormat(format);
-
- String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType;
- Files.copy(is, Paths.get(tmpDir, attachmentFName));
- return URI.create(String.format("juick://%s", attachmentFName));
- } catch (IOException e) {
- logger.error(String.format("Failed download image by url: %s", url.toString()), e);
- throw e;
- }
- }
-}
diff --git a/src/main/java/com/juick/server/util/ImageUtils.java b/src/main/java/com/juick/server/util/ImageUtils.java
deleted file mode 100644
index e06339ba..00000000
--- a/src/main/java/com/juick/server/util/ImageUtils.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import com.juick.model.Attachment;
-import org.apache.commons.imaging.ImageReadException;
-import org.apache.commons.imaging.Imaging;
-import org.apache.commons.imaging.common.ImageMetadata;
-import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
-import org.apache.commons.imaging.formats.tiff.TiffField;
-import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.imgscalr.Scalr;
-import org.imgscalr.Scalr.Rotation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReader;
-import javax.imageio.stream.ImageInputStream;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.util.Iterator;
-
-public class ImageUtils {
- private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);
-
- private String imgDir;
- private String tmpDir;
-
- public ImageUtils(String imgDir, String tmpDir) {
- this.imgDir = imgDir;
- this.tmpDir = tmpDir;
- }
-/**
- * Returns <code>BufferedImage</code>, same as <code>ImageIO.read()</code> does.
- *
- * <p>JPEG images with EXIF metadata are rotated according to Orientation tag.
- *
- * @param imageFile a <code>File</code> to read from.
- */
- private static BufferedImage readImageWithOrientation(File imageFile)
- throws IOException {
-
- BufferedImage image = ImageIO.read(imageFile);
- if (!FilenameUtils.getExtension(imageFile.getName()).equals("jpg")) {
- return image;
- }
-
- try {
- ImageMetadata metadata = Imaging.getMetadata(imageFile);
-
- if (metadata instanceof JpegImageMetadata) {
- JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
- TiffField orientationField = jpegMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION);
-
- if (orientationField != null) {
- int orientation = orientationField.getIntValue();
- switch (orientation) {
- case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW:
- image = Scalr.rotate(image, Rotation.CW_90);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_ROTATE_180:
- image = Scalr.rotate(image, Rotation.CW_180);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW:
- image = Scalr.rotate(image, Rotation.CW_270);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL:
- image = Scalr.rotate(image, Rotation.FLIP_HORZ);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_MIRROR_VERTICAL:
- image = Scalr.rotate(image, Rotation.FLIP_VERT);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW:
- image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_90);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW:
- image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_270);
- break;
- case TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL:
- default:
- // do nothing
- break;
- }
- }
- }
- } catch (ImageReadException | IOException e) {
- // failed to read metadata.
- // nothing to do here, return image as is.
- }
-
- return image;
- }
-
- public void saveImageWithPreviews(String tempFilename, String outputFilename)
- throws IOException {
- String ext = FilenameUtils.getExtension(outputFilename);
-
- Path outputImagePath = Paths.get(imgDir, "p", outputFilename);
- // this throws strange exceptions
- // Files.move(Paths.get(tmpDir, tempFilename), outputImagePath);
- FileUtils.moveFile(Paths.get(tmpDir, tempFilename).toFile(), outputImagePath.toFile());
- BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile());
-
- int width = originalImage.getWidth();
- int height = originalImage.getHeight();
- int maxDimension = Math.max(width, height);
- BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage;
- BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage;
- BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage;
- ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile());
- ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile());
- ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile());
- }
-
- public void saveAvatar(String tempFilename, int uid)
- throws IOException {
- String ext = FilenameUtils.getExtension(tempFilename);
- String originalName = String.format("%s.%s", uid, ext);
- Path originalPath = Paths.get(imgDir, "ao", originalName);
- Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING);
- BufferedImage originalImage = ImageIO.read(originalPath.toFile());
-
- String targetExt = "png";
- String targetName = String.format("%s.%s", uid, targetExt);
- ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile());
- ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile());
- }
- public Attachment getAttachment(File imgFile) throws IOException {
- Attachment attachment = new Attachment();
- try (ImageInputStream stream = ImageIO.createImageInputStream(imgFile)) {
- Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
- while (iter.hasNext()) {
- ImageReader reader = iter.next();
- try {
- reader.setInput(stream);
- attachment.setWidth(reader.getWidth(reader.getMinIndex()));
- attachment.setHeight(reader.getHeight(reader.getMinIndex()));
- return attachment;
- } catch (Exception e) {
- logger.debug("Error reading {}, trying next reader", imgFile.getAbsolutePath());
- } finally {
- reader.dispose();
- }
- }
- }
-
- logger.warn("Not a known image file {}", imgFile.getAbsolutePath());
- return attachment;
- }
-} \ No newline at end of file
diff --git a/src/main/java/com/juick/server/util/TagUtils.java b/src/main/java/com/juick/server/util/TagUtils.java
deleted file mode 100644
index 754d8020..00000000
--- a/src/main/java/com/juick/server/util/TagUtils.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import com.juick.model.Tag;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Created by aalexeev on 11/13/16.
- */
-public class TagUtils {
- private TagUtils() {
- throw new IllegalStateException();
- }
-
- public static String toString(final List<Tag> tags) {
- if (CollectionUtils.isEmpty(tags))
- return StringUtils.EMPTY;
-
- return tags.stream().map(t -> "*" + t.getName())
- .collect(Collectors.joining(" "));
- }
-}
diff --git a/src/main/java/com/juick/server/util/WebUtils.java b/src/main/java/com/juick/server/util/WebUtils.java
deleted file mode 100644
index bc3ac63a..00000000
--- a/src/main/java/com/juick/server/util/WebUtils.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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
- * 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.util;
-
-import java.util.regex.Pattern;
-
-/**
- * Created by aalexeev on 11/28/16.
- */
-public class WebUtils {
- private WebUtils() {
- throw new IllegalStateException();
- }
-
- private static final Pattern USER_NAME_PATTERN = Pattern.compile("[a-zA-Z-_\\d]{2,16}");
-
- private static final Pattern POST_NUMBER_PATTERN = Pattern.compile("-?\\d+");
-
- private static final Pattern JID_PATTERN = Pattern.compile("^[a-zA-Z0-9\\\\-\\\\_\\\\@\\\\.]{6,64}$");
-
-
- public static boolean isPostNumber(final String aString) {
- return aString != null && POST_NUMBER_PATTERN.matcher(aString).matches();
- }
-
- public static boolean isNotPostNumber(final String aString) {
- return !isPostNumber(aString);
- }
-
- public static boolean isUserName(final String aString) {
- return aString != null && USER_NAME_PATTERN.matcher(aString).matches();
- }
-
- public static boolean isNotUserName(final String aString) {
- return !isUserName(aString);
- }
-
- public static boolean isJid(final String aString) {
- return aString != null && JID_PATTERN.matcher(aString).matches();
- }
-
- public static boolean isNotJid(final String aString) {
- return !isJid(aString);
- }
-
-
-}
diff --git a/src/main/java/com/juick/server/xmpp/JidConverter.java b/src/main/java/com/juick/server/xmpp/JidConverter.java
deleted file mode 100644
index fdf80108..00000000
--- a/src/main/java/com/juick/server/xmpp/JidConverter.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
- * 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;
-
-import org.springframework.core.convert.converter.Converter;
-import org.springframework.lang.Nullable;
-import rocks.xmpp.addr.Jid;
-
-import javax.annotation.Nonnull;
-
-public class JidConverter implements Converter<String, Jid> {
- @Nullable
- @Override
- public Jid convert(@Nonnull String jidStr) {
- return Jid.of(jidStr);
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java b/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java
deleted file mode 100644
index c973b624..00000000
--- a/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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
- * 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.iq;
-
-import javax.xml.bind.annotation.XmlRootElement;
-
-@XmlRootElement(name = "query")
-public class MessageQuery {
- private MessageQuery() {
-
- }
-}
diff --git a/src/main/java/com/juick/server/xmpp/iq/package-info.java b/src/main/java/com/juick/server/xmpp/iq/package-info.java
deleted file mode 100644
index 822fb8c4..00000000
--- a/src/main/java/com/juick/server/xmpp/iq/package-info.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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
- * 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/>.
- */
-
-@XmlAccessorType(XmlAccessType.FIELD)
-@XmlSchema(namespace = "http://juick.com/query#messages", elementFormDefault = XmlNsForm.QUALIFIED)
-package com.juick.server.xmpp.iq;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlNsForm;
-import javax.xml.bind.annotation.XmlSchema; \ No newline at end of file